Exciting New Features React 16.3: Bye componentWillReceiveProps, Hello New Context API!

Finally, React 16.3 is out! We've all been anxiously waiting for this new React update, well, I definitely have. Maybe you are wondering what's all the hype about? No worries I got you. In this article I highlight the exciting new features that are part of this release. Note: I will not include all the new features, just the most interesting ones. In case you want to know all the changes in this release find that info here . I'll keep it short and concise and give a few links that will be helpful to those who want to know more about the inner workings of the new features. Enjoy!

Which new features will we look into?

React adopted the RFC process (request for comments) for contributing new ideas. This means anyone can create a RFC document with a proposal and submit a PR into the RFC repository. This proposal will go through several rounds of discussion and if accepted will be merged into React. I'll share the RFC phase document links for the new features; that might be helpful if you want to find out what the thought process behind these features were.

  1. The new context API - RFC phase
  2. Lifecycle method changes - RFC phase
  3. StrictMode Component

Context API

Context API is a way we can achieve state management within React. It allows us to pass data down the component tree without having to involve each intermediate i.e. no prop-drilling.

Prop-drilling is the condition where you find yourself having to pass down props through components that don't use those props just to get them to the appropriate child components

The old Context API, has always been an experimental API and in the official React docs it has been clearly stated that devs should steer clear of using it because it is likely to break future releases of React. This means for state management we had to use third-party libraries like redux, mobX, flux etc. With the new, officially supported Context API we can eliminate this overhead and just use React.

Usage of the new API

We access this API using React.createContext().

const ThemeContext = React.createContext({
  background: "blue"
})

We provide a default value for createContext.

Calling this factory function returns an object that has two components:

  • a Provider - this component provides data to all components in the subtree
  • a Consumer - this component consumes/utilizes data passed in by the provider
class App extends React.Component {
  constructor(props) {
    this.state = {
      value: {background: "black"},
    };
  }

  render() {
    return (
      <ThemeContext.Provider value={this.state.value}>
        <TodoList />
      </ThemeContext.Provider>
    )
  }
}

In our case we are passing our theme context to the App component and set a value that we want to pass {background: "black"}. Any component we render within App can access this context.

To access the context in a child component of App we need a matching consumer. We'll create a matching consumer inside TodoList.

const TodoList = () => {
  <ThemeContext.Consumer>
    {(context) => {
      return (
        <div style={{background: context.background}}>
          <li>
            <ul>Todo 1</ul>
          </li>
        </div>
      )
    }}
  </ThemeContext.Consumer>
}

Any matching consumer in a provider's sub-tree can access the this context value no matter how deeply nested it is. This means if we had many children rendered in App we only need to call ThemeContext.Consumer to access the context values.

Notice: The consumer uses render props API. If you aren't familiar with this pattern it would be great if you read up on it. You could also use a HOC (Higher Order Component) if you prefer. An advantage of using render props is that it de-clutters the prop namespace.

To update the context value, the parent re-renders and passes a different value to the child components.

In the case where a "consumer" is rendered without a matching "provider" as its parent, the default value initialized on createContext is used.

Note: The introduction of the new context API does not mean that the old context API cannot be used anymore. If you had some part of your code that used it, it'll still work. You can use both instances side by side. The APIs are different enough to eliminate any confusion.

Lifecycle method changes

The React team has decided to deprecate some lifecycle methods:

  1. componentWillMount
  2. componentWillReceiveProps
  3. componentWillUpdate

The good news is that they are replaced with some static methods that make it easier to write async-compatible React components. The deprecated methods will continue to work till version 17 so don't worry too much.

In future versions to use the legacy lifecycle methods we'll need to use the UNSAFE_ prefix. At the moment however both the old lifecycle names and the new aliases are supported. The new aliases are: UNSAFE_componentWillMount, UNSAFE_componentWillReceiveProps and UNSAFE_componentWillUpdate.

New lifecycle methods include:

  1. getDerivedStateFromProps - this is a safer alternative to componentWillReceiveProps
  2. getSnapshotBeforeUpdate - can be used to safely read properties from e.g. the DOM before updates

static getDerivedStateFromProps

getDerivedStateFromProps is a static method.

A static method is one that belongs to a class not to the instance of that class. Simply put a static method does not have access to this.

class Example extends React.Component {
  // has the static keyword before the method
  static getDerivedStateFromProps(nextProps, prevState) {
    // ...
  }
}

This method is called:

  • after a component is instantiated
  • when the component receives new props

It returns an object to update state or null to indicate that no state updates are required.

In this way you can use it instead of componentWillRecieveProps. If you called both componentWillRecieveProps and getDerivedStateFromProps only getDerivedStateFromProps gets called.

getSnapshotBeforeUpdate

This lifecycle method is called right before mutations are made. Its return value is passed as a third parameter in componentDidUpdate.

It covers the use cases componentWillUpdate. What can you use it for? If for example you needed to manually preserve scroll positions during re-renders. You can use getSnapshotBeforeUpdate and componentDidUpdate.

class Example extends React.Component {
  getSnapshotBeforeUpdate(prevProps, prevState) {
    // ...
  }
}

StrictMode Component

This component does not render any UI but instead adds checks and warnings for its children components. It is a way to make sure your code follows the best possible practices especially for async rendering.

Example of how to enable strict mode:

import { StrictMode } from "react";

class App extends React.Component {
  constructor(props) {
    this.state = {
      value: {background: "black"},
    };
  }

  render() {
    return (
      <StrictMode>
        <ThemeContext.Provider value={this.state.value}>
          <TodoList />
        </ThemeContext.Provider>
      </StrictMode>
    )
  }
}

You can enable strict mode for a part your React tree, if you want it doesn't have to be enabled for all your components.

It catches instances where:

Conclusion

Many of the changes occuring within React are to accomodate async rendering which Dan Abramov teased during the JSConf Iceland 2018, link to his presentation here. Its going to be amazing, so keep your eyes peeled on the React blog to learn of more.

I hope this article sparked enough interest to go experiment and learn more about the new features. Also, now that you know about the RFC process feel free to suggest new features that you feel might benefit the community.