React async rendering, a new exciting built in feature, is coming in react version 17. While we don’t know the exact time frame, it may well be very soon. For existing projects, most likely you will have some preparation and migration works to finish before you can take advantage of this new cool feature. We finished the migration and sure like to share our experience.
Deprecated lifecycle methods
- componentWillMount
- componentWillReceiveProps
- compnentWillUpdate
They discourage use of these methods because any potential misuse could be problematic with async rendering.
componentWillMount migration
- Data initialization including component state should go to constructor or property initializer.
- Event subscriptions or any thing that needs clean up at componentWillUnmount should be done at componentDidMount because invoking componentWillMount does not actually guarantee that componentWillUnmount will be called. But componentDidMount does.
- Moves data fetching to componentDidMount. When we perform this migration, we actually also try to adopt paradigm of separation of content and presentation. What that means is that we move data fetching completely outside the component. As a result, the component only relies on props for data and does not have to worry about fetching data. We achieve this by kicking off simultaneous data fetch when there is a route change.
componentWillReceiveProps migration
- The recommended way to update state in response to props changes is to use the new static getDerivedStateFromProps(props, state) lifecycle method. This static method does not offer previous props. If you must compare with some previous props, you would need to keep a copy yourself in component state.
- Move any side effect (including data fetching) or external callbacks to componentDidUpdate because componentWillReceiveProps may get called more than once for a single update.
componentWillUpdate
Similar to componentWillReceiveProps, use componentDidUpdate instead.
New lifecycle methods
- static getDerivedStateFromProps(props, state)
- getSnapshotBeforeUpdate(prevProps, prevState)
These new methods are for more advance use cases. In general, try to avoid using them if you can.
static getDerivedStateFromProps(props, state)
This method is called before the component is rendered. You can derive new state based on the props use new state as return object. However, the best migration is not to use it. If you found yourself using it, make sure you check out reactjs.org blog post You Probably Don’t Need Derived State.
The major problem could arise when your component state may change in respond to user’s actions or props change. In this situation, updating component state using getDerivedStateFromProps (called when parent component rerenders, even wihtout props change) could create undesired conflicts and result in incorrect component state. This essentially is a difficulty of having more than 1 source of truth. The best approach is to maintain a single source of truth. That is, the component should be either fully controlled (data only through props), or fully uncontrolled (data maintained only in state). In either case, you don’t need to use getDerivedStateFromProps.
getSnapshotBeforeUpdate(prevProps, prevState)
This method is called before DOM is updated, allowing you to keep track of DOM data like scroll position. Return value will be the third parameter of componentDidUpdate.
Some other code cleanup
While we perform the migration, we also utilize some nice new features from react version 16 and newer javascript constructs.
Fragment
Have you wrapped the return value of render() with redundant div or span, just to keep it as a single element? You can now use React.Fragment instead to avoid this extra div/span to show up in DOM.
1 2 3 4 5 6 7 8 |
render() { return ( <Fragment> <h1>Heading</h1> This is content! </Fragment> ); } |
ReactDOM.hydrate
Don’t forget to use ReactDOM.hydrate instead of ReactDOM.render if you are incorporating server side rendering.
javascript arrow function
Have you used bind() in a component class to bind a function to the class? Define function using arrow function and you don’t need to bind:
1 2 3 4 5 |
onClick = (evt) => {} render() { return <div onClick={this.onClick}>Hello</div>; } |