React Fiber (React v16) is awesome and has taken some significant steps towards improving the developer experience as well as the quality of applications built with React. In this post, we will look at how React 17 looks to build on React v16.
With a strong focus on asynchronous rendering, React 17 looks to create an environment for developers to build efficient applications easily by making it possible to minimize the impact of computing and network speeds on the user experience.
To achieve this, there will inevitably be some shift in the way React apps are written through the introduction of some new features and deprecation of some previous ones.
Here are the changes we can expect:
Let’s look at them one by one.
Replacing the deprecated lifecycle methods are two new lifecycle methods, getDerivedStateFromProps
and getSnapShotBeforeUpdate
.
These are essential to keep in mind if you are preparing to migrate your application to React 17. A great starting point is replacing the unsafe methods with these new lifecycle methods. For instance, componentWillUpdate
can be replaced by using getDerivedStateFromProps
in conjunction with shouldComponentUpdate
, componentWillMount
should be removed altogether for async rendering.
Here’s a diagram of what the component lifecycle will look like after React v16.4 with the unsafe methods removed.
This lifecycle method replaces componentWillReceiveProps
and componentWillUpdate
and will be called after a component is created and when it received new props. It returns an object to update state when props change or null
when there is no change in state.
state = { cachedSomeProp: null }; static getDerivedStateFromProps(nextProps, prevState) { // do things with nextProps.someProp and prevState.cachedSomeProp return { cachedSomeProp: nextProps.someProp, .. }; }
However, keep in mind that this lifecycle method is only meant to be used in cases where a component needs to update its local state following a change in props. Unnecessary use of this method may introduce some bugs such as unconditionally copying props to state and erasing state when props change as was the case with componentWillReceiveProps
.
This handles component updates and will effectively replace componentWillUpdate
and works with componentDidUpdate
. It is called before any DOM updates and returns a value that is passed to componentDidUpdate
which then handles the changes:
class ScrollingList extends React.Component { listRef = null; getSnapshotBeforeUpdate(prevProps, prevState) { // Are we adding new items to the list? // Capture the scroll position so we can adjust scroll later. if (prevProps.list.length < this.props.list.length) { return ( this.listRef.scrollHeight - this.listRef.scrollTop ); } return null; } componentDidUpdate(prevProps, prevState, snapshot) { // If we have a snapshot value, we've just added new items. // Adjust scroll so these new items don't push the old ones out of view. // (snapshot here is the value returned from getSnapshotBeforeUpdate) if (snapshot !== null) { this.listRef.scrollTop = this.listRef.scrollHeight - snapshot; } } render() { return ( <div ref={this.setListRef}> {/* ...contents... */} </div> ); } setListRef = ref => { this.listRef = ref; }; }
Time slicing is the biggest update to React that looks to improve user experience. It aims to make sites more friendly by allowing prioritized rendering of elements. So high priority updates aren’t blocked because of rendering low priority updates.
Dan Abramov likened this to version control at JSConf Iceland 2018. Where work is split into branches and the work of each branch is merged to master once complete. Similarly, with time slice, elements can be rendered separately allowing the app to have ready elements rendered and a placeholder for elements yet to be rendered.
A good example is a case where we are fetching users’ details. The user avatar and name will display and their bio comes in later. Time slice will allow us to display the ready details(name and avatar) and have a placeholder while the bio is fetched. Once the bio is ready it renders and the final state is as expected. This enhances the responsiveness of the app on slower devices and networks and also means the rest of the app can still be used while a certain element is still rendering.
How is this achieved? Through a new API called Suspense that was introduced in React 16.6.
Suspense is responsible for rendering the fallback UI(placeholder) while state updates are being prepared. It holds back rendering of the final UI until these updates are ready, rendering a placeholder of your choice in the meantime. This is where you would place your spinner if that’s what you plan to use to indicate a loading state.
This provides a great alternative to conditional rendering to handling data loading. You can also set a time limit and if the data loads within this time the fallback UI will not be rendered in concurrent mode. Awesome, right? Even better, Suspense was introduced in React 16.6, so Suspense is ready to use right now.
Here’s a simple example of Suspense in use:
const DataComponent = React.lazy(() => import('./DataComponent')); function MySuspenseComponent(){ return ( <Suspense fallback={<Spinner />}> <DataComponnent/> <Suspense> ) }
In the above example, Suspense is used in conjunction with lazy() which lazy loads the DataComponent, while the component is loading, a spinner will display and our component only shows up after loading is complete. We can also do this using other async operations such as API calls.
Also expected with React 17 is a stable version of the react-cache library which will help expand on the functionality of Suspense and allow the use of async operations with synchronous operations. Here’s what react-cache may look like:
const getInfo = () => fetch("https://myApi.apiexample").then(res => res.json()) const ApiResource = createResource(getInfo) const SayHello = () => { const data = ApiResource.read() return <div>Hi {data.name}</div> } const App = () => ( <Suspense fallback={<Spinner />}> <SayHello /> </Suspense> )
The react-cache may also be used with time slicing to load data in the background as a low priority update, a good example is when displaying data as tabs, this makes the data instantly available when switching between tabs. Andrew Clark gave a great demo of such as a use case at React Conf 2018, check it out below:
React 17 is a major release with several reasons for React developers to get excited. Not only does it provide some amazing, new features that will redefine how React applications are built, but it also expands on recently introduced features such as Hooks with incremental changes that allow developers to make better use of them. This will lead to better applications and a better experience building them. It is exciting to see what else the future holds for React.
You may also want to check out Dan Abramov’s talk at JS Conf 2018 which I referred to when compiling this article.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn to build scalable micro-frontend applications using React, discussing their advantages over monolithic frontend applications.
Build a fully functional, real-time chat application using Laravel Reverb’s backend and Vue’s reactive frontend.
console.time is not a function
errorExplore the two variants of the `console.time is not a function` error, their possible causes, and how to debug.
jQuery 4 proves that jQuery’s time is over for web developers. Here are some ways to avoid jQuery and decrease your web bundle size.