New Features in React 16
We are in the process of upgrading to React 16 at HireVue. Below is an opinionated list of the features that seem cool to me.
Fragments
version 16.0
This may seem crazy, but prior to R16, the render method would only accept a single dom node as the outer element. In many cases, such as lists of elements, this added unnecessary markup, making layout and styling needlessly complicated. Here are fragments in action:
render() {
return (
<>
<Summer />
<Morty />
<EvilMorty />
</>
);
}
Yep, those are empty tags in the JSX. That’s the syntax. Those empty tags get replaced with nothing in the html output. In React, these will show as <React.Fragment/>
elements.
One important note: This syntax does not accept any attributes. For those you’ll need the Fragment
tag. This is especially important for mapping arrays, as each element in the array requires a unique key:
{ricks.map(rick => (
<Fragment key={item.id}>
<div>{rick.morty}</div>
</Fragment>
))}
Portals
version 16.0
Portals allow components to render to any DOM node you desire, rather that into the parent component they are placed. Two problems I’ve had in the past I can see this solving: z-index and relative positioning problems. I could also see this being used to place items in the DOM in a nice flow for accessibility.
render() {
return ReactDOM.createPortal(
this.props.children,
domNode
);
}
Context
version 16.3
Context is interesting. It’s a state store that doesn’t need to pass attributes down the tree. It seems like React’s replacement for Redux. It definitely has less boilerplate, but also seems shockingly simple, which could be limiting. Here is the “so simple and not at all applicable to a large scale app” example in the React docs:
const ThemeContext = React.createContext('light');
class ThemeProvider extends React.Component {
state = {theme: 'light'};
render() {
return (
<ThemeContext.Provider value={this.state.theme}>
{this.props.children}
</ThemeContext.Provider>
);
}
}
class ThemedButton extends React.Component {
render() {
return (
<ThemeContext.Consumer>
{theme => <Button theme={theme} />}
</ThemeContext.Consumer>
);
}
}
So you have a Provider
component, which replaces the Redux store. You then have a Consumer
component that can access this store as long as it’s nested in the Provider:
class App extends React.Component {
render() {
return (
<ThemeProvider>
<ThemedButton />
</ThemeProvider>
);
}
}
If you wanted an action creator, it’s as simple as setting the state in the Provider component:
const ThemeContext = React.createContext('light');
class ThemeProvider extends React.Component {
state = {theme: 'light'};
changeTheme = theme => {
this.setState({theme});
};
render() {
return (
<ThemeContext.Provider
value={{
...this.state,
changeTheme: this.changeTheme,
}}
>
{this.props.children}
</ThemeContext.Provider>
);
}
}
class ThemedButton extends React.Component {
render() {
return (
<ThemeContext.Consumer>
{({theme, changeTheme}) => <Button theme={theme} onClick={changeTheme('dark')} />}
</ThemeContext.Consumer>
);
}
}
Really, I’m not sure if this will replace Redux at this point for large scale applications. It looks super clean for smaller things though. Much less boilerplate than Redux.
Suspense
version 16.6
Ok, let’s unpack this quickly. React.lazy
let’s you dynamically import components, only loading their associated bundles when they are needed. This can help in applications with larger bundles, like ours at HireVue.
Suspense
lets you show a fallback if a dynamic import using React.lazy
hasn’t finished loading yet.
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}
Error Boundaries
version 16.0
When a component throws a Javascript error, in the past it would crash your entire React app. With Error Boundaries, you can gracefully handle these errors, and keep them contained to specific parts of your application. Two lifecycle methods, getDerivedStateFromError
and componentDidCatch
are used for fallback UI and logging errors, respectively.
This example best illustrates Error Boundaries.
Hooks
version 16.8
The new hotness. Hooks let you use a bunch of React’s features without classes. It’s worth reading the motivations behind hooks and getting away from classes on the React blog.
Hooks let you manage state, and interact with lifecycle methods via the useState and useEffect hooks, which we’ll look at quickly below. Finally, you can write your own custom hooks, which can replace HOCs, and don’t add more components to the component tree.
State Hook
import React, { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You have created {count} Meeseeks, Jerry.</p>
<button onClick={() => setCount(count + 1)}>Meeseeks Box</button>
</div>
);
}
Here we’ve setup a state variable called count
. useState
is returning a pair, and the second item in the array is the function to update the state variable.
Further reading on state hooks.
Effect Hook
The effect hook performs the purposes of componentDidMount
, componentDidUpdate
, and componentWillUnmount
, all rolled into one API. This allows you to streamline how you perform “side effects” such as fetching data, manually changing the DOM, etc.
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `${count} Meeseeks`;
return () => {
document.title = `Existence is pain to a Meeseeks, Jerry!`;
};
});
return (
<div>
<p>You have created {count} Meeseeks, Jerry.</p>
<button onClick={() => setCount(count + 1)}>Meeseeks Box</button>
</div>
);
}
Here we are updating the document title with an effect hook. Since effect hooks run on every render, including the first, this is taking the place of both componentDidMount
and componentDidUpdate
.
Finally, you can return a function in your effect to perform when it unmounts, “effectively” replacing componentWillUnmount
.
There is a ton more to hooks, including writing your own. I highly suggest reading more on the topic.
Questions
These are the areas I want to learn more around with R16.
- Can context replace Redux for large scale apps like ours?
- Will lazy allow us to make our bundle splitting much more granular?
- Will hooks change how often we write classes?
- Will hooks replace many of our HOCs?
- How will these new features affect some of our home grown tools?
- Which new features are worth a deeper dive with our team?
Leave a Reply