Initializing State
export default class ProfileContainer extends Component {
state = { expanded: false }
propTypes and defaultProps
export default class ProfileContainer extends Component {
state = { expanded: false }
static propTypes = {
model: object.isRequired,
title: string
}
static defaultProps = {
model: {
id: 0
},
title: 'Your Name'
}
Methods
this approach is cleaner and easier, maintaining the correct context automatically via the ES6 arrow function
export default class ProfileContainer extends Component {
state = { expanded: false }
static propTypes = {
model: object.isRequired,
title: string
}
static defaultProps = {
model: {
id: 0
},
title: 'Your Name'
}
handleSubmit = (e) => {
e.preventDefault()
this.props.model.save()
}
handleNameChange = (e) => {
this.props.model.changeName(e.target.value)
}
handleExpand = (e) => {
e.preventDefault()
this.setState({ expanded: !this.state.expanded })
}
Passing setState a Function
setState — it’s actually asynchronous. React batches state changes for performance reasons, so the state may not change immediately after setState is called
That means you should not rely on the current state when calling setState — since you can’t be sure what that state will be!
Here’s the solution — pass a function to setState, with the previous state as an argument.
this.setState({ expanded: !this.state.expanded })
// below is best practice
this.setState(prevState => ({ expanded: !prevState.expanded }))
Destructuring Props
Components with many props should have each prop on a newline, like above.
render() {
const {
model,
title
} = this.props
return (
....
)
}
Decorators
Decorators are flexible and readable way of modifying component functionality.
you can decorate your class components like so — which is the same as passing the component into a function.
Closures
Avoid passing new closures to subcomponents, like so:
<input
type="text"
value={model.name}
// onChange={(e) => { model.name = e.target.value }}
// ^ Not this. Use the below:
onChange={this.handleChange}
placeholder="Your Name"/>
Here’s why: every time the parent component renders, a new function is created and passed to the input
If the input were a React component, this would automatically trigger it to re-render, regardless of whether its other props have actually changed.
Functional Components
These components have no state and no methods. They’re pure, and easy to reason about. Use them as often as possible.
ExpandableForm.propTypes = {
onSubmit: func.isRequired,
expanded: bool,
onExpand: func.isRequired
}
// way 1
function ExpandableForm(props) {
const formStyle = props.expanded ? {height: 'auto'} : {height: 0}
return (
<form style={formStyle} onSubmit={props.onSubmit}>
{props.children}
<button onClick={props.onExpand}>Expand</button>
</form>
)
}
// way 2 but avoid use this way
function ExpandableForm({ onExpand, expanded = false, children, onSubmit }) {
const formStyle = expanded ? {height: 'auto'} : {height: 0}
return (
<form style={formStyle} onSubmit={onSubmit}>
{children}
<button onClick={onExpand}>Expand</button>
</form>
)
}
Avoid the following ES6 syntax:
const ExpandableForm = ({ onExpand, expanded, children }) => {
Looks very modern, but the function here is actually unnamed.
1、This lack of name will not be a problem if your Babel is set up correctly — but if it’s not, any errors will show up as occurring in <
2、Unnamed functions can also cause problems with Jest, a React testing library. Due to the potential for difficult-to-understand bugs (and the lack of real benefit) we recommend using function instead of const
Wrapping
Since you can’t use decorators with functional components, you simply pass it the function in as an argument:
export default observer(ExpandableForm)