Introduction to React 16 Features — Part 1
Should you be using React 16? A detailed intro to React 16’s powerful new features
Introduction
React 16, a.k.a React Fiber has brought a new level of simplicity to the development game.
With features like Error Boundaries, Portals, and Context API, React Developers can build their apps with a kind of ease never seen before.
In this 2-part series where I’ll be explaining some of the new features of React 16 and the best practices to work with everything React 16 has to offer.
Don’t forget to install the latest versions of React and React DOM.
Useful tip: Use Bit to encapsulate components with all their dependencies and setup. Build truly modular applications with better code reuse, simpler maintenance and less overhead.
Handle your Errors with Error Boundaries
Error Boundaries — What are they?
React 16’s Error boundaries help you make sure that an error in part of your UI won’t break the whole app. Error boundaries basically does 3 things:
- It catches JavaScript errors in the child component tree
- It logs these errors
- It displays the fallback UI
In this section, I will show you how to create an Error Boundary for your app.
Example App
I have created a new React app using create-react-app
and I’ve written the following code in my App.js
file.
What is happening in this App?
This is a simple application that renders a Profile
. The Profile
component expects a user
object with the property name
passed inside it. I also have a button that updates the user. Run yarn start
and see that it works perfectly.
But, the app will break down when you click on the button.
This happens because the onClick
function of the button is passing a null
value for the user
. This will throw and Uncaught TypeError
since the app cannot read the property name of null
.
Solution
To handle such errors, React 16 has introduced a new lifecycle method called componentDidCatch
. This method will allow us to catch any JavaScript errors from anywhere inside a component’s child component tree.
In order to inform me that something is not working as it should, I will update my state inside this lifecycle method, and indicate that an error has occurred.
Make sure to add hasError: false
in your state
object.
Then in the render
function, add a conditional statement that checks the value of hasError
and renders the fallback UI.
It’s important to include this behavior because errors which are not caught will result in an unmount of the whole React component tree.
This wasn’t the case in React 15 and lower, as the UI stayed untouched there. This behavior has changed because the React team felt that it would be worse to leave the corrupted UI in place rather than completely removing it.
Coming back to componentDidCatch
, it receives two arguments that will allow us to track and investigate our errors. These arguments are:
error
— This is the error instance itselfinfo
— This argument contains the component stack.
This is especially useful when you’e running your app in production mode, as it will use an error reporting service to check exactly where the component broke down.
With this, the App
component can be described as an error boundary. The next step would be to extract the error boundary’s functionality into a separate component, so that we can use it everywhere in our app without having to write the code again.
Error Boundary
Create a new component called ErrorBoundary
as shown here:
This new component contains the hasError state and the componentDidCatch lifecycle method. The app’s render method has the fallback UI for when an error occurs.
Remove the hasError: false
and componentDidCatch
method from the App
component. Inside the render method, wrap the Profile
inside the ErrorBoundary
component like this:
Error boundaries work with deeply nested component structures. So it would best to put it in a few strategic places, rather than at every level.
The error boundary is now capable of catching errors and rendering the fallback UI!
Render Multiple Elements without using a Wrapping Element Component
Before React 16
Before React 16, we had to make sure that our render
method returns a single React element. So in order to return multiple elements, we needed to wrap them up inside a <div></div>
element.
With React 16, it is now possible to return an Array of elements. This opens up a whole new bag of tricks for component design.
App
Here, I have created an app that returns a list of superheroes.
As you can see, I have wrapped the list inside a <div>
element. Let’s create a new function component that contains another list of superheroes.
const Heroes = () => [
<li key="1">The Flash</li>,
<li key="2">Aquaman</li>,
<li key="3">Wonder Woman</li>,
]
I will render this component inside the unordered list in the App
component.
render() {
return (
<div>
<ul>
<li>Batman</li>
<li>Superman</li>
<Heroes/>
</ul>
</div>
)
}
With this, if you run yarn start
, you will see that all of them are rendered inside the same unordered list element.
You can also render the list inside a class component. I will create a new class component called moreHeroes
. This component will render two more heroes.
class MoreHeroes extends Component {
render() {
return [
<li key="1">Green Lantern</li>,
<li key="2">Green Arrow</li>
]
}
}
As you can see here, I have used the same keys in both Heroes
and moreHeroes
components. So if I add this component inside the App
component, it will render inside the same DOM element and there won’t be any kind of key warning in the console.
Also…
Another great feature of React 16 is that you can return the past children
themselves without a wrapping element. I will create a new component that will just return props.children
.
const DC = props => props.children
I will use this component to wrap all the entries in my Heroes
component. By doing so, we won’t need to use commas to render multiple elements.
const Heroes = () =>
<DC>
<li key="1">The Flash</li>
<li key="2">Aquaman</li>
<li key="3">Wonder Woman</li>
</DC>;
Render Text Only Components
In React 15 and below, we had to wrap our text in a needless <span>
or <div>
tag. But React 16 has taken away that unneeded structure.
App
First I am going to create a new component that simply renders some text. It currently contains a wrapping <span>
as required by components in React 15 and below.
const Text = ({text }) => {
const Tex = text;
return <span>{Tex}</span>;
};
I can then use this component inside the App
component like this:
class App extends Component {
render() {
return (
<div>
<Text text="A Generic hello world text" />
</div>
);
};
}
If you look at the DOM structure of your app, you will see that the component is wrapped inside a <div>
and a <span>
.
While the <span>
doesn’t cause any problems, it also doesn’t do much anything else for us. So it would make our DOM much cleaner and a bit more lightweight without it.
With React 16, we can update the Text
component to just return a plain text.
const Text = ({text}) => {
const Tex = text;
return Tex;
};
If you inspect the DOM now, you will find the unwanted span
has gone.
Portals
By default, a React component tree directly maps to the DOM tree. This can work against you when your app has UI elements like overlays and loading bars.
React 16 takes away this limiting factor by allowing you to attach parts of the component tree inside a different root element.
App
Let’s create a simple App
component that renders an <h1>
tag.
class App extends Component {
render() {
return (
<div>
<h1>Dashboard</h1>
</div>
)
}
}
createPortal()
I will use the {ReactDOM.createPortal()}
below the <h1>
tag. This function accepts two arguments:
- The new subtree containing component — Here I will create a
<div>
element containing the text “Welcome”. - The target container. I am going to query for an element with the
id
ofportal-container
.
The App
component will then look like this:
class App extends Component {
render() {
return (
<div>
<h1>Dashboard</h1>
{ReactDOM.createPortal(
<div>Welcome</div>,
document.getElementById('portal-container');
)}
</div>
);
}
}
Inside public/index.html
, create a new <div>
element with an id
of portal-container
below the root
id.
If you look at the DOM structure, you will see that the welcome text is rendered inside the portal-container
. This comes in very handy for things like overlays.
Now add a className="overlay"
property to the welcome text. This will link the <div>
to these style properties:
Our app will look now like this:
Overlay
This approach will need us to extend index.html
. We can also give our component the control to add and remove new DOM elements right at the end of the body. To do this, I will extract the ReactDOM.createPortal()
function into a new component.
class Overlay extends Component {
constructor(props) {
super(props);
this.overlayContainer = document.createElement('div');
document.body.appendChild(this.overlayContainer);
}
render() {
return ReactDOM.createPortal(
<div className="overlay">{this.props.children}</div>,
this.overlayContainer
);
}
}
I am creating an overlayContainer
inside the component’s constructor method. I will append it to the document’s body. The render
function will not need a wrapping element and I can directly return the portal. The portal renders the overlay
div and the children that are passed to it inside the App
. The portal’s target is the overlay container that we created in the constructor.
When using portals, make sure that your code is properly cleaned and doesn’t contain any unnecessary <div>
elements.
componentWillUnmount
I also want to be able to remove this overlay. So I will add a componentWillUnmount
lifecycle method to the Overlay
component. Inside I will tell React to remove the child from the document’s body.
componentWillUnmount() {
document.body.removeChild(this.overlayContainer);
}
Inside the render function, I will add a <span>
element which when clicked will cause the overlay to close. Make sure to add it inside the overlay
div.
<span onClick={this.props.onClose}>x</span>
Next, inside the App
component, I will create a constructor
and add the overlayActive
state as true
. I will also add a closeOverlay
function that sets the overlayActive
to false
.
constructor(props) {
super(props);
this.state = { overlayActive: true }
}
closeOverlay = () => {
this.setState({overlayActive: false})
}
Next I will add a condition inside the render, which basically says that the overlay should only be displayed if overlayActive
is true. I am also going to link the closeOverlay
function to overlay’s onClose
.
render() {
return (
<div>
<h1>Dashboard</h1>
{this.state.overlayActive &&
<Overlay onClose={this.closeOverlay}>
<div>Welcome</div>
</Overlay>}
</div>
);
}
Run the yarn start
command and you will see it work perfectly!