React 16 — What can it do for you? — Part 1

May 14, 2018 0 Comments

React 16 — What can it do for you? — Part 1

 

 

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.

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.

I have created a new React app using create-react-app and I’ve written the following code in my App.js file.

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.

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 itself
  • info — 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.

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!

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.

Here, I have created an app that returns a list of superheroes.

App.js

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.

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>;

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.

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.

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.

Let’s create a simple App component that renders an <h1> tag.

class App extends Component {
render() {
return (
<div>
<h1>Dashboard</h1>
</div>
)
}
}

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 of portal-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:

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.

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!

This concludes the Part 1 of 2 of this series. Part 2 will be out soon, so hold on tight :) Meanwhile, you are welcome to read more on Bit’s Blog.


Tag cloud