This guide applies to client-rendered code in /app
.
Try to follow the Airbnb Javascript Guide.
The Airbnb eslint config has been enabled minus jsx-filename-extension and react/forbid-prop-types.
We also use Prettier.
Follow the pattern:
// absolute imports
import React from 'react'
import PropTypes from 'prop-types'
// then a newline
// then relative imports
import { MyComponent } from "../../MyComponent";
// then a newline
// then css modules
import styles from './MyStyles.scss'
// destructure within function
// call the parameter props
const MyForm = props => {
const { aProp, anotherProp } = props
return (
<div>
</div>
)
}
// include defaults for non-required
MyForm.defaultProps = {
aProp: ''
}
// define a type for every prop
MyForm.propTypes = {
anotherProp: PropTypes.number.isRequired,
aProp: PropTypes.string
}
export class MyClass extends Component {
// use static proptypes
static propTypes = {
isLoading: PropTypes.bool,
errors: PropTypes.arrayOf(string).isRequired
};
// and static defaultProps
static defaultProps = {
isLoading: false
};
// destructure inside render
render() {
const { isLoading, errors } = this.props;
return (
<div>
</div>
)
}
Define the base page route in routes.js
<Switch>
<Route path={`${rootPath}/myPage`} component={MyPage} />
Pages are classes, do sub-routing, redux, and pass props to content components.
Ensure you handle sub route default.
class MyPage extends Component {
static propTypes = {
propA: PropTypes.string.isRequired,
propB: PropTypes.bool.isRequired
}
componentWillMount() {
this.props.loadData()
}
render() {
const { propA, propB } = this.props
<Switch>
<Route
exact path={match.url}
render={() =>
<MyForm
propA={propA}
propB={propB}
/>}
/>
<Route component={NotFound} />
</Switch>
}
}
const mapStateToProps = state => {
return {
propA: state.myValue
}
}
const mapDispatchToProps = dispatch => {
return {
loadData: () => dispatch(myAction)
}
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MyPage))
Content components are stateless:
const MyForm = props => {
const { propA, propB } = props
return (
<div>
</div>
)
}
MyForm.propTypes = {
propA: PropTypes.string.isRequired,
propB: PropTypes.bool.isRequired
}
Use a traditional redux structure.
Seperate files for actions, actions creators and reducers. Ensure the file names match.
export const ADD_TODO = 'ADD_TODO'
import { ADD_TODO } from '../../constants'
export const addTodo = text => ({
type: ADD_TODO,
text: 'hello world'
})
case ADD_TODO:
return {
...state,
todos: [
...state.todos,
{
text: action.text,
completed: false
}
]
})
The base styles come from uikit 2.
Modifications to base styles can be found in custom.css.
Use css modules for component-specific styles.
Ensure camelCase
style names.
.loadingIndicator {
position: relative;
width: 60px;
height: 60px;
}
Where possible, provide at least a shallow snapshot test or render sanity check for a component.
Place tests alongside components. Ensure test file name matches component and ends with .test.js