What's New in React v16.6.0
The v16.6.0 release of React includes some highly anticipated features such as
native support for dynamic imports and memoization for function components. This
is a big release for the library and community at large. Some of these features
will have a major impact on how we compose UI moving forward.
This post will focus on covering the features I find most intriguing:
React.lazyReact.SuspenseReact.memostatic contextType
Let's dive in!
React.lazy
The term lazy loading has been around for a while, and the idea is pretty simple: to load some slice of code when it's needed. It is a technique used in code splitting, which involves breaking up your app into separate bundles which can be loaded on demand.
With the React.lazy API, you can now load components dynamically - only when
they need to render.
import React, { lazy } from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
React.lazy takes a function that returns a dynamic import. The module being
imported
must have a default export containing a React component.
In the example above, LazyComponent is not an actual component...yet. It's a
dynamic import that can be rendered in your JSX as if it were a component.
import React, { lazy } from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
function App() {
return (
<div>
<LazyComponent />
</div>
);
}
When App renders, LazyComponent will be loaded in a separate bundle. But
what happens if it hasn't loaded by the time App is finished rendering?
Error: An update was suspended, but no placeholder UI was provided.
That happens. Not ideal. Luckily, there is a simple solution in the form of
React.Suspense.
React.Suspense
To prevent the error above from happening, you can wrap a lazy component in the
new React.Suspense component, which provides some fallback UI to show while
the component loads.
import React, { lazy, Suspense } from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}
React.Suspense requires a single fallback prop, and its value should be a
React element you wish to show while a dynamically imported component is being
loaded.
You don't need to render a React.Suspense component directly above a
lazy-loaded component. Instead, React will look for the closest React.Suspense
parent and use its fallback UI.
<Suspense fallback={<div>Loading...</div>}>
<div>
<div>
<LazyComponent />
</div>
</div>
</Suspense>
Another cool feature about React.Suspense is that it can be used to wrap
multiple components being dynamically imported with React.lazy. It's not
limited to just one.
<Suspense fallback={<div>Loading...</div>}>
<LazyOne />
<LazyTwo />
</Suspense>
There is a clear synergy between React.lazy and React.Suspense. You could
have done dynamic importing before with something like webpack, but it wasn't
baked into React itself. The APIs are super friendly too, and together they
should make code splitting more approachable.
React.memo
According to wikipedia, memoization is an optimization technique that stores the results of expensive function calls and returns the cached result when the same inputs occur again.
My introduction to memoization happened with the Redux library
reselect. In reselect there is a
createSelector function which optimizes expensive calculations that rely on
inputs from a Redux store. If you're not familiar with memoization then I
encourage you to take a look at the
Motivation for Memoized Selectors
section of the reselect docs. Consider it required reading. 📚
So what does this mean in the context of React? Up until this point, function
components in React re-render every time their parent renders, even if their
prop values haven't changed. To solve this, React v16.6 introduces a new
higher-order component called React.memo. You use it the same way you would
use any higher-order component in React, except it is only to be used on
function components.
import React, { memo } from 'react';
const MemoizeMeCaptain = ({ prop1, prop2 }) => <h1>Get it!</h1>;
export default memo(MemoizeMeCaptain);
If MemoizeMeCaptain receives the same values for prop1 and prop2, it will
not re-render. This is a performance optimization and should be used in the same
way you would use React.PureComponent for class components. You can even
provide your own
comparison function as the
second argument to React.memo if you want more control.
Egghead.io has a great
free lesson
on how to use React.memo by Elijah Manor.
It's definitely worth a look! 👀
static contextType
The last new feature I want to touch on is static contextType, which is a new
way of subscribing to context in class components. If you're not familiar with
context in React, the official docs
are a great introduction.
Consuming context in a component requires you to use a render prop. I never really had a problem with this method, but I can definitely understand the argument that it creates unnecessary nesting and muddies up the component tree.
render() {
return (
<MyContext.Provider>
{context => (
<h1>{context.value}</h1>
)}
</MyContext.Provider>
)
}
In React v16.6 the static contextType can be used on class components to
consume context without requiring you to use render props at all!
class App extends Component {
static contextType = MyContext;
render() {
return <h1>{this.context.value}</h1>;
}
}
this.context is also accessible in lifecycle methods, which is a major quality
of life adjustment. Before, you'd have to jump through hoops to consume context
in those methods.
class App extends Component {
static contextType = MyContext;
componentDidMount() {
console.log(this.context.value);
}
// ...
}
The only drawback I've found with this feature, and one that is mentioned in the React docs, is that you can only consume a single context. That being said, there are creative workarounds for those who need it.
A quick note on static properties
I've touched on static properties in
a previous article.
One important takeaway from that article, and one that still holds true, is that
you can only use static properties in React class components if you're using
@babel/plugin-proposal-class-properties.
For you create-react-app users, this feature is
enabled by default.
👍🏻
If you're not able to use @babel/plugin-proposal-class-properties, there is
another way of declaring a contextType on a class:
class App extends Component {
render() {
return <h1>{this.context.value}</h1>;
}
}
App.contextType = MyContext;
Final thoughts
v16.6.0 of React is packed with some great features. I'm so excited for the
future of this library. It's already a joy to use, but the core team continues
to pump out great work to make our lives so much easier. And with everything
announced at ReactConf 2018 last week, this is just
the tip of the iceberg.
It's a great time to be a developer working in the React ecosystem. Reach out to me on Twitter and let me know what your favorite new features are!