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.lazy
React.Suspense
React.memo
static 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!