LearnNew Features in React 16

Guil Hernandez
writes on October 6, 2017

Facebook recently announced the release of React 16, a complete rewrite of the React library. The new core architecture, codenamed “Fiber,” keeps most of the API intact and backward compatible. You can update your existing apps to React 16 without running into issues.

Fiber introduces lots of new, highly anticipated features to this newest version of React. In this post, I’ll cover notable features like error boundaries, portals, new render return types, and more.

Error Handling

One of the biggest changes is how React handles JavaScript errors. Before version 16, a JavaScript error inside a component would break the entire app.

React 16 provides a built-in solution for handling errors gracefully, with a new lifecycle method called componentDidCatch(). This lifecycle hook gets triggered whenever a component’s render() function throws an error.

With componentDidCatch() comes a new concept of an Error Boundary. Error boundaries are wrapper components that use componentDidCatch() to capture errors anywhere in their child component tree.

For example, you create an error boundary as a class component:

class ErrorBoundary extends Component {
  state = { 
    error: false 
  };

  componentDidCatch(error, info) {
    this.setState({ error: true });
  }

  render() {
    if (this.state.error) {
      // Fallback UI
      return <h1>Yikes, something went wrong!</h1>
    }
    return this.props.children;
  }
}

You can wrap the error boundary around your entire app, or specific components:

<ErrorBoundary>
  <MyForm />
</ErrorBoundary>

The ErrorBoundary component uses componentDidCatch() to call setState() and display fallback UI when an error occurs anywhere in its child component tree. It can catch errors during rendering, in lifecycle methods, even in the constructor of a child component.

Any errors caught in a component tree get “reported” up to the error boundary’s componentDidCatch(error, info) method where it can be sent to an error reporting service. The method takes two arguments to help you track and investigate errors: error (the error instance) and info (info containing the component stack).

componentDidCatch(error, info) {
  this.setState({ error: true });

  // Log error to error reporting service
  logError(error, info);
}

As you can see, the new componentDidCatch() method works like a catch {} block for React components. It provides a more consistent and dependable way to catch and deal with errors. Learn more about error handling on the React blog.

Render Arrays, Strings and Numbers

React 16 no longer requires that a render method returns a single React element. You’re now able to render multiple sibling elements without a wrapping element by returning an array. For example, an array of list items:

const JSTeachers = () => {
  return [
    <li key="1">Treasure</li>,
    <li key="2">Joel</li>,
    <li key="3">James</li>
  ];
}

Since you’re returning an array, you should give each element in the array a unique key prop, otherwise React will still display a console warning.

You can render the list items inside the <ul> of another component, alongside other items, and React merges them inside the same list:

const Teachers = () => {
  return (
    <ul>
      <li>Alena</li>
      <JSTeachers />
      <li>Nick</li>
      <li>Ben</li>
    </ul>
  );
}

Example teachers list output.

Here’s another example of how it can return multiple sibling components via an array:

const App = () => {
  return [
    <Header key="head" />,
    <Main key="main" />,
    <Footer key="foot" />,
  ];
}

Eliminating any divs or spans that were added just for your React component tree leads to overall cleaner and smaller components.

React 16 can also return a string or number from a render method:

// Header
render() {
  return 'Hello from the Header';
}

// Footer
render() {
  return 2017;
}

Portals

React 16 lets you insert a child element into a different location in the DOM, outside of your main DOM tree, with “portals.” For example, in your index.html template, you’ll usually have a div with the id ‘root’, which is where your entire app is rendered into the DOM via ReactDOM.render():

ReactDOM.render(<App />, document.getElementById('root'));

With the new portals feature, you can add a second element that’s a sibling of your ‘root’ div:

<body>
  <div id="root"></div>
  <div id="my-portal"></div>
<body>

You can render specific parts of your component tree inside the sibling div via the new function createPortal(child, container).

return (
  ReactDOM.createPortal(
    <h1>Hello from inside the portal!</h1>,
    document.getElementById('my-portal')
  )
);

The function’s first argument (child) is the content you want to render, and the second argument (container) is the DOM element where the content is going to render.

As mentioned in the docs, portals are useful “when a parent component has an overflow: hidden or z-index style, but you need the child to visually ‘break out’ of its container.” A handy use case for portals is when creating modal windows.

class Modal extends Component {
  render() {
    return (
      ReactDOM.createPortal(
        this.props.children,
        document.getElementById('my-portal')
      )
    );
  }
}

Since Modal returns a portal via createPortal(), you can place it anywhere in your app and it will always render inside the “my-portal” div.

class App extends Component {
  render() {
    return (
      <div>
        <header>
          <h1>Welcome to my App!</h1>
        </header>
        <Modal>
	 {/* Modal content */}
        </Modal>
      </div>
    );
  }
}

Custom DOM Attributes

Before React 16, any unrecognized attribute given to an element would be ignored by React. Now, any standard or custom attribute will end up in the DOM. For example, you can provide a custom attribute to a DOM component, like a div:

<div my-attribute="is-cool">...</div>

React will pass the attribute through to the DOM. Learn more about why this change was made in this article.

Even though all attributes get rendered to the DOM, you should still use the canonical React naming for known attributes. For example, if you provide class and tabindex to a DOM component, React will warn you about using tabIndex and className instead.

Returning null from setState

React 16 now lets you decide if state gets updated in setState to prevent unnecessary updates. In your events, you can check if the new value of state is the same as the existing one. If the values are the same, you can return null and it won’t re-render the component. For example:

getCourse = course => {
  const newTitle = course.title;
  this.setState(state => {
    if (state.title === newTitle) {
      return null;
    } else {
      return { title: newTitle };
    }
  });
};

If the values are different, the component re-renders with the new state.

MIT Licensed

A welcomed change is that React is now available under the MIT license! The uncertainties created by the previous BSD+Patents license was a concern to many developers, startups and companies using React.

Now React is more flexible. You can use React in your project without having to worry about the previous patent’s clause. If you’re not able to upgrade immediately to version 16, React 15.6.2 was also published under MIT.

Final Thoughts on React 16

You can start using React 16 today! When you create a new app with Create React App, it will automatically install react and react-dom version 16.

Even though it is backwards compatible, it does come with minor breaking changes. However, React engineers assure us that the changes only affect uncommon use cases and they don’t expect them to break most apps. You can view all the breaking changes here.

Learn how to build React apps at Treehouse

Leave a Reply

Want to learn more about Javascript?

Learn how to use JavaScript to add interactivity to websites.

Learn more