Quantcast
Channel: React
Viewing all 193 articles
Browse latest View live

Update on Async Rendering

$
0
0

For over a year, the React team has been working to implement asynchronous rendering. Last month during his talk at JSConf Iceland, Dan unveiled some of the exciting new possibilities async rendering unlocks. Now we’d like to share with you some of the lessons we’ve learned while working on these features, and some recipes to help prepare your components for async rendering when it launches.

One of the biggest lessons we’ve learned is that some of our legacy component lifecycles tend to encourage unsafe coding practices. They are:

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

These lifecycle methods have often been misunderstood and subtly misused; furthermore, we anticipate that their potential misuse may be more problematic with async rendering. Because of this, we will be adding an “UNSAFE_” prefix to these lifecycles in an upcoming release. (Here, “unsafe” refers not to security but instead conveys that code using these lifecycles will be more likely to have bugs in future versions of React, especially once async rendering is enabled.)

Gradual Migration Path

React follows semantic versioning, so this change will be gradual. Our current plan is:

  • 16.3: Introduce aliases for the unsafe lifecycles, UNSAFE_componentWillMount, UNSAFE_componentWillReceiveProps, and UNSAFE_componentWillUpdate. (Both the old lifecycle names and the new aliases will work in this release.)
  • A future 16.x release: Enable deprecation warning for componentWillMount, componentWillReceiveProps, and componentWillUpdate. (Both the old lifecycle names and the new aliases will work in this release, but the old names will log a DEV-mode warning.)
  • 17.0: Remove componentWillMount, componentWillReceiveProps, and componentWillUpdate . (Only the new “UNSAFE_” lifecycle names will work from this point forward.)

Note that if you’re a React application developer, you don’t have to do anything about the legacy methods yet. The primary purpose of the upcoming version 16.3 release is to enable open source project maintainers to update their libraries in advance of any deprecation warnings. Those warnings will not be enabled until a future 16.x release.

We maintain over 50,000 React components at Facebook, and we don’t plan to rewrite them all immediately. We understand that migrations take time. We will take the gradual migration path along with everyone in the React community.


Migrating from Legacy Lifecycles

If you’d like to start using the new component APIs introduced in React 16.3 (or if you’re a maintainer looking to update your library in advance) here are a few examples that we hope will help you to start thinking about components a bit differently. Over time, we plan to add additional “recipes” to our documentation that show how to perform common tasks in a way that avoids the problematic lifecycles.

Before we begin, here’s a quick overview of the lifecycle changes planned for version 16.3:

  • We are adding the following lifecycle aliases: UNSAFE_componentWillMount, UNSAFE_componentWillReceiveProps, and UNSAFE_componentWillUpdate. (Both the old lifecycle names and the new aliases will be supported.)
  • We are introducing two new lifecycles, static getDerivedStateFromProps and getSnapshotBeforeUpdate.

New lifecycle: getDerivedStateFromProps

class Example extends React.Component {
  static getDerivedStateFromProps(nextProps, prevState) {
    // ...
  }
}

The new static getDerivedStateFromProps lifecycle is invoked after a component is instantiated as well as when it receives new props. It can return an object to update state, or null to indicate that the new props do not require any state updates.

Together with componentDidUpdate, this new lifecycle should cover all use cases for the legacy componentWillReceiveProps.

New lifecycle: getSnapshotBeforeUpdate

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

The new getSnapshotBeforeUpdate lifecycle is called right before mutations are made (e.g. before the DOM is updated). The return value for this lifecycle will be passed as the third parameter to componentDidUpdate. (This lifecycle isn’t often needed, but can be useful in cases like manually preserving scroll position during rerenders.)

Together with componentDidUpdate, this new lifecycle should cover all use cases for the legacy componentWillUpdate.

You can find their type signatures in this gist.

We’ll look at examples of how both of these lifecycles can be used below.

Examples

Note

For brevity, the examples below are written using the experimental class properties transform, but the same migration strategies apply without it.

Initializing state

This example shows a component with setState calls inside of componentWillMount:

// Before
class ExampleComponent extends React.Component {
  state = {};

  componentWillMount() {
    this.setState({
      currentColor: this.props.defaultColor,
      palette: 'rgb',
    });
  }
}

The simplest refactor for this type of component is to move state initialization to the constructor or to a property initializer, like so:

// After
class ExampleComponent extends React.Component {
  state = {
    currentColor: this.props.defaultColor,
    palette: 'rgb',
  };
}

Fetching external data

Here is an example of a component that uses componentWillMount to fetch external data:

// Before
class ExampleComponent extends React.Component {
  state = {
    externalData: null,
  };

  componentWillMount() {
    this._asyncRequest = asyncLoadData().then(
      externalData => {
        this._asyncRequest = null;
        this.setState({externalData});
      }
    );
  }

  componentWillUnmount() {
    if (this._asyncRequest) {
      this._asyncRequest.cancel();
    }
  }

  render() {
    if (this.state.externalData === null) {
      // Render loading state ...
    } else {
      // Render real UI ...
    }
  }
}

The above code is problematic for both server rendering (where the external data won’t be used) and the upcoming async rendering mode (where the request might be initiated multiple times).

The recommended upgrade path for most use cases is to move data-fetching into componentDidMount:

// After
class ExampleComponent extends React.Component {
  state = {
    externalData: null,
  };

  componentDidMount() {
    this._asyncRequest = asyncLoadData().then(
      externalData => {
        this._asyncRequest = null;
        this.setState({externalData});
      }
    );
  }

  componentWillUnmount() {
    if (this._asyncRequest) {
      this._asyncRequest.cancel();
    }
  }

  render() {
    if (this.state.externalData === null) {
      // Render loading state ...
    } else {
      // Render real UI ...
    }
  }
}

There is a common misconception that fetching in componentWillMount lets you avoid the first empty rendering state. In practice this was never true because React has always executed render immediately after componentWillMount. If the data is not available by the time componentWillMount fires, the first render will still show a loading state regardless of where you initiate the fetch. This is why moving the fetch to componentDidMount has no perceptible effect in the vast majority of cases.

Note

Some advanced use-cases (e.g. libraries like Relay) may want to experiment with eagerly prefetching async data. An example of how this can be done is available here.

In the longer term, the canonical way to fetch data in React components will likely be based on the “suspense” API introduced at JSConf Iceland. Both simple data fetching solutions and libraries like Apollo and Relay will be able to use it under the hood. It is significantly less verbose than either of the above solutions, but will not be finalized in time for the 16.3 release.

When supporting server rendering, it’s currently necessary to provide the data synchronously – componentWillMount was often used for this purpose but the constructor can be used as a replacement. The upcoming suspense APIs will make async data fetching cleanly possible for both client and server rendering.

Adding event listeners (or subscriptions)

Here is an example of a component that subscribes to an external event dispatcher when mounting:

// Before
class ExampleComponent extends React.Component {
  componentWillMount() {
    this.setState({
      subscribedValue: this.props.dataSource.value,
    });

    // This is not safe; it can leak!
    this.props.dataSource.subscribe(
      this.handleSubscriptionChange
    );
  }

  componentWillUnmount() {
    this.props.dataSource.unsubscribe(
      this.handleSubscriptionChange
    );
  }

  handleSubscriptionChange = dataSource => {
    this.setState({
      subscribedValue: dataSource.value,
    });
  };
}

Unfortunately, this can cause memory leaks for server rendering (where componentWillUnmount will never be called) and async rendering (where rendering might be interrupted before it completes, causing componentWillUnmount not to be called).

People often assume that componentWillMount and componentWillUnmount are always paired, but that is not guaranteed. Only once componentDidMount has been called does React guarantee that componentWillUnmount will later be called for clean up.

For this reason, the recommended way to add listeners/subscriptions is to use the componentDidMount lifecycle:

// After
class ExampleComponent extends React.Component {
  state = {
    subscribedValue: this.props.dataSource.value,
  };

  componentDidMount() {
    // Event listeners are only safe to add after mount,
    // So they won't leak if mount is interrupted or errors.
    this.props.dataSource.subscribe(
      this.handleSubscriptionChange
    );

    // External values could change between render and mount,
    // In some cases it may be important to handle this case.
    if (
      this.state.subscribedValue !==
      this.props.dataSource.value
    ) {
      this.setState({
        subscribedValue: this.props.dataSource.value,
      });
    }
  }

  componentWillUnmount() {
    this.props.dataSource.unsubscribe(
      this.handleSubscriptionChange
    );
  }

  handleSubscriptionChange = dataSource => {
    this.setState({
      subscribedValue: dataSource.value,
    });
  };
}

Sometimes it is important to update subscriptions in response to property changes. If you’re using a library like Redux or MobX, the library’s container component should handle this for you. For application authors, we’ve created a small library, create-subscription, to help with this. We’ll publish it along with React 16.3.

Rather than passing a subscribable dataSource prop as we did in the example above, we could use create-subscription to pass in the subscribed value:

import {createSubscription} from 'create-subscription';

const Subscription = createSubscription({
  getCurrentValue(sourceProp) {
    // Return the current value of the subscription (sourceProp).
    return sourceProp.value;
  },

  subscribe(sourceProp, callback) {
    function handleSubscriptionChange() {
      callback(sourceProp.value);
    }

    // Subscribe (e.g. add an event listener) to the subscription (sourceProp).
    // Call callback(newValue) whenever a subscription changes.
    sourceProp.subscribe(handleSubscriptionChange);

    // Return an unsubscribe method.
    return function unsubscribe() {
      sourceProp.unsubscribe(handleSubscriptionChange);
    };
  },
});

// Rather than passing the subscribable source to our ExampleComponent,
// We could just pass the subscribed value directly:
<Subscription source={dataSource}>
  {value => <ExampleComponent subscribedValue={value} />}
</Subscription>;

Note

Libraries like Relay/Apollo should manage subscriptions manually with the same techniques as create-subscription uses under the hood (as referenced here) in a way that is most optimized for their library usage.

Updating state based on props

Here is an example of a component that uses the legacy componentWillReceiveProps lifecycle to update state based on new props values:

// Before
class ExampleComponent extends React.Component {
  state = {
    isScrollingDown: false,
  };

  componentWillReceiveProps(nextProps) {
    if (this.props.currentRow !== nextProps.currentRow) {
      this.setState({
        isScrollingDown:
          nextProps.currentRow > this.props.currentRow,
      });
    }
  }
}

Although the above code is not problematic in itself, the componentWillReceiveProps lifecycle is often mis-used in ways that do present problems. Because of this, the method will be deprecated.

As of version 16.3, the recommended way to update state in response to props changes is with the new static getDerivedStateFromProps lifecycle. (That lifecycle is called when a component is created and each time it receives new props):

// After
class ExampleComponent extends React.Component {
  // Initialize state in constructor,
  // Or with a property initializer.
  state = {
    isScrollingDown: false,
    lastRow: null,
  };

  static getDerivedStateFromProps(nextProps, prevState) {
    if (nextProps.currentRow !== prevState.lastRow) {
      return {
        isScrollingDown:
          nextProps.currentRow > prevState.lastRow,
        lastRow: nextProps.currentRow,
      };
    }

    // Return null to indicate no change to state.
    return null;
  }
}

You may notice in the example above that props.currentRow is mirrored in state (as state.lastRow). This enables getDerivedStateFromProps to access the previous props value in the same way as is done in componentWillReceiveProps.

You may wonder why we don’t just pass previous props as a parameter to getDerivedStateFromProps. We considered this option when designing the API, but ultimately decided against it for two reasons:

  • A prevProps parameter would be null the first time getDerivedStateFromProps was called (after instantiation), requiring an if-not-null check to be added any time prevProps was accessed.
  • Not passing the previous props to this function is a step toward freeing up memory in future versions of React. (If React does not need to pass previous props to lifecycles, then it does not need to keep the previous props object in memory.)

Note

If you’re writing a shared component, the react-lifecycles-compat polyfill enables the new getDerivedStateFromProps lifecycle to be used with older versions of React as well. Learn more about how to use it below.

Invoking external callbacks

Here is an example of a component that calls an external function when its internal state changes:

// Before
class ExampleComponent extends React.Component {
  componentWillUpdate(nextProps, nextState) {
    if (
      this.state.someStatefulValue !==
      nextState.someStatefulValue
    ) {
      nextProps.onChange(nextState.someStatefulValue);
    }
  }
}

Sometimes people use componentWillUpdate out of a misplaced fear that by the time componentDidUpdate fires, it is “too late” to update the state of other components. This is not the case. React ensures that any setState calls that happen during componentDidMount and componentDidUpdate are flushed before the user sees the updated UI. In general, it is better to avoid cascading updates like this, but in some cases they are necessary (for example, if you need to position a tooltip after measuring the rendered DOM element).

Either way, it is unsafe to use componentWillUpdate for this purpose in async mode, because the external callback might get called multiple times for a single update. Instead, the componentDidUpdate lifecycle should be used since it is guaranteed to be invoked only once per update:

// After
class ExampleComponent extends React.Component {
  componentDidUpdate(prevProps, prevState) {
    if (
      this.state.someStatefulValue !==
      prevState.someStatefulValue
    ) {
      this.props.onChange(this.state.someStatefulValue);
    }
  }
}

Side effects on props change

Similar to the example above, sometimes components have side effects when props change.

// Before
class ExampleComponent extends React.Component {
  componentWillReceiveProps(nextProps) {
    if (this.props.isVisible !== nextProps.isVisible) {
      logVisibleChange(nextProps.isVisible);
    }
  }
}

Like componentWillUpdate, componentWillReceiveProps might get called multiple times for a single update. For this reason it is important to avoid putting side effects in this method. Instead, componentDidUpdate should be used since it is guaranteed to be invoked only once per update:

// After
class ExampleComponent extends React.Component {
  componentDidUpdate(prevProps, prevState) {
    if (this.props.isVisible !== prevProps.isVisible) {
      logVisibleChange(this.props.isVisible);
    }
  }
}

Fetching external data when props change

Here is an example of a component that fetches external data based on props values:

// Before
class ExampleComponent extends React.Component {
  state = {
    externalData: null,
  };

  componentDidMount() {
    this._loadAsyncData(this.props.id);
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.id !== this.props.id) {
      this.setState({externalData: null});
      this._loadAsyncData(nextProps.id);
    }
  }

  componentWillUnmount() {
    if (this._asyncRequest) {
      this._asyncRequest.cancel();
    }
  }

  render() {
    if (this.state.externalData === null) {
      // Render loading state ...
    } else {
      // Render real UI ...
    }
  }

  _loadAsyncData(id) {
    this._asyncRequest = asyncLoadData(id).then(
      externalData => {
        this._asyncRequest = null;
        this.setState({externalData});
      }
    );
  }
}

The recommended upgrade path for this component is to move data updates into componentDidUpdate. You can also use the new getDerivedStateFromProps lifecycle to clear stale data before rendering the new props:

// After
class ExampleComponent extends React.Component {
  state = {
    externalData: null,
  };

  static getDerivedStateFromProps(nextProps, prevState) {
    // Store prevId in state so we can compare when props change.
    // Clear out previously-loaded data (so we don't render stale stuff).
    if (nextProps.id !== prevState.prevId) {
      return {
        externalData: null,
        prevId: nextProps.id,
      };
    }

    // No state update necessary
    return null;
  }

  componentDidMount() {
    this._loadAsyncData(this.props.id);
  }

  componentDidUpdate(prevProps, prevState) {
    if (this.state.externalData === null) {
      this._loadAsyncData(this.props.id);
    }
  }

  componentWillUnmount() {
    if (this._asyncRequest) {
      this._asyncRequest.cancel();
    }
  }

  render() {
    if (this.state.externalData === null) {
      // Render loading state ...
    } else {
      // Render real UI ...
    }
  }

  _loadAsyncData(id) {
    this._asyncRequest = asyncLoadData(id).then(
      externalData => {
        this._asyncRequest = null;
        this.setState({externalData});
      }
    );
  }
}

Note

If you’re using an HTTP library that supports cancellation, like axios, then it’s simple to cancel an in-progress request when unmounting. For native Promises, you can use an approach like the one shown here.

Reading DOM properties before an update

Here is an example of a component that reads a property from the DOM before an update in order to maintain scroll position within a list:

class ScrollingList extends React.Component {
  listRef = null;
  previousScrollHeight = null;

  componentWillUpdate(nextProps, nextState) {
    // Are we adding new items to the list?
    // Capture the current height of the list so we can adjust scroll later.
    if (this.props.list.length < nextProps.list.length) {
      this.previousScrollHeight = this.listRef.scrollHeight;
    }
  }

  componentDidUpdate(prevProps, prevState) {
    // If previousScrollHeight is set, we've just added new items.
    // Adjust scroll so these new items don't push the old ones out of view.
    if (this.previousScrollHeight !== null) {
      this.listRef.scrollTop +=
        this.listRef.scrollHeight -
        this.previousScrollHeight;
      this.previousScrollHeight = null;
    }
  }

  render() {
    return (
      <div ref={this.setListRef}>
        {/* ...contents... */}
      </div>
    );
  }

  setListRef = ref => {
    this.listRef = ref;
  };
}

In the above example, componentWillUpdate is used to read the DOM property. However with async rendering, there may be delays between “render” phase lifecycles (like componentWillUpdate and render) and “commit” phase lifecycles (like componentDidUpdate). If the user does something like resize the window during this time, the scrollHeight value read from componentWillUpdate will be stale.

The solution to this problem is to use the new “commit” phase lifecycle, getSnapshotBeforeUpdate. This method gets called immediately before mutations are made (e.g. before the DOM is updated). It can return a value for React to pass as a parameter to componentDidUpdate, which gets called immediately after mutations.

The two lifecycles can be used together like this:

class ScrollingList extends React.Component {
  listRef = null;

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // Are we adding new items to the list?
    // Capture the current height of the list so we can adjust scroll later.
    if (prevProps.list.length < this.props.list.length) {
      return this.listRef.scrollHeight;
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // If we have a snapshot value, we've just added new items.
    // Adjust scroll so these new items don't push the old ones out of view.
    // (snapshot here is the value returned from getSnapshotBeforeUpdate)
    if (snapshot !== null) {
      this.listRef.scrollTop +=
        this.listRef.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      <div ref={this.setListRef}>
        {/* ...contents... */}
      </div>
    );
  }

  setListRef = ref => {
    this.listRef = ref;
  };
}

Note

If you’re writing a shared component, the react-lifecycles-compat polyfill enables the new getSnapshotBeforeUpdate lifecycle to be used with older versions of React as well. Learn more about how to use it below.

Other scenarios

While we tried to cover the most common use cases in this post, we recognize that we might have missed some of them. If you are using componentWillMount, componentWillUpdate, or componentWillReceiveProps in ways that aren’t covered by this blog post, and aren’t sure how to migrate off these legacy lifecycles, please file a new issue against our documentation with your code examples and as much background information as you can provide. We will update this document with new alternative patterns as they come up.

Open source project maintainers

Open source maintainers might be wondering what these changes mean for shared components. If you implement the above suggestions, what happens with components that depend on the new static getDerivedStateFromProps lifecycle? Do you also have to release a new major version and drop compatibility for React 16.2 and older?

Fortunately, you do not!

When React 16.3 is published, we’ll also publish a new npm package, react-lifecycles-compat. This package polyfills components so that the new getDerivedStateFromProps and getSnapshotBeforeUpdate lifecycles will also work with older versions of React (0.14.9+).

To use this polyfill, first add it as a dependency to your library:

# Yarn
yarn add react-lifecycles-compat

# NPM
npm install react-lifecycles-compat --save

Next, update your components to use the new lifecycles (as described above).

Lastly, use the polyfill to make your component backwards compatible with older versions of React:

import React from 'react';
import polyfill from 'react-lifecycles-compat';

class ExampleComponent extends React.Component {
  static getDerivedStateFromProps(nextProps, prevState) {
    // Your state update logic here ...
  }
}

// Polyfill your component to work with older versions of React:
polyfill(ExampleComponent);

export default ExampleComponent;

React v16.3.0: New lifecycles and context API

$
0
0

A few days ago, we wrote a post about upcoming changes to our legacy lifecycle methods, including gradual migration strategies. In React 16.3.0, we are adding a few new lifecycle methods to assist with that migration. We are also introducing new APIs for long requested features: an official context API, a ref forwarding API, and an ergonomic ref API.

Read on to learn more about the release.

Official Context API

For many years, React has offered an experimental API for context. Although it was a powerful tool, its use was discouraged because of inherent problems in the API, and because we always intended to replace the experimental API with a better one.

Version 16.3 introduces a new context API that is more efficient and supports both static type checking and deep updates.

Note

The old context API will keep working for all React 16.x releases, so you will have time to migrate.

Here is an example illustrating how you might inject a “theme” using the new context API:

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

Learn more about the new context API here.

createRef API

Previously, React provided two ways of managing refs: the legacy string ref API and the callback API. Although the string ref API was the more convenient of the two, it had several downsides and so our official recommendation was to use the callback form instead.

Version 16.3 adds a new option for managing refs that offers the convenience of a string ref without any of the downsides:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);

    this.inputRef = React.createRef();
  }

  render() {
    return <input type="text" ref={this.inputRef} />;
  }

  componentDidMount() {
    this.inputRef.current.focus();
  }
}

Note:

Callback refs will continue to be supported in addition to the new createRef API.

You don’t need to replace callback refs in your components. They are slightly more flexible, so they will remain as an advanced feature.

Learn more about the new createRef API here.

forwardRef API

Higher-order components (or HOCs) are a common way to reuse code between components. Building on the theme context example from above, we might create an HOC that injects the current “theme” as a prop:

function withTheme(Component) {
  return function ThemedComponent(props) {
    return (
      <ThemeContext.Consumer>
        {theme => <Component {...props} theme={theme} />}
      </ThemeContext.Consumer>
    );
  };
}

We can use the above HOC to wire components up to the theme context without having to use ThemeContext directly. For example:

class FancyButton extends React.Component {
  buttonRef = React.createRef();

  focus() {
    this.buttonRef.current.focus();
  }

  render() {
    const {label, theme, ...rest} = this.props;
    return (
      <button
        {...rest}
        className={`${theme}-button`}
        ref={this.buttonRef}>
        {label}
      </button>
    );
  }
}

const FancyThemedButton = withTheme(FancyButton);

// We can render FancyThemedButton as if it were a FancyButton
// It will automatically receive the current "theme",
// And the HOC will pass through our other props.
<FancyThemedButton
  label="Click me!"
  onClick={handleClick}
/>;

HOCs typically pass props through to the components they wrap. Unfortunately, refs are not passed through. This means that we can’t attach a ref to FancyButton if we use FancyThemedButton— so there’s no way for us to call focus().

The new forwardRef API solves this problem by providing a way for us to intercept a ref and forward it as a normal prop:

function withTheme(Component) {
  // Note the second param "ref" provided by React.forwardRef.
  // We can attach this to Component directly.
  function ThemedComponent(props, ref) {
    return (
      <ThemeContext.Consumer>
        {theme => (
          <Component {...props} ref={ref} theme={theme} />
        )}
      </ThemeContext.Consumer>
    );
  }

  // These next lines are not necessary,
  // But they do give the component a better display name in DevTools,
  // e.g. "ForwardRef(withTheme(MyComponent))"
  const name = Component.displayName || Component.name;
  ThemedComponent.displayName = `withTheme(${name})`;

  // Tell React to pass the "ref" to ThemedComponent.
  return React.forwardRef(ThemedComponent);
}

const fancyButtonRef = React.createRef();

// fancyButtonRef will now point to FancyButton
<FancyThemedButton
  label="Click me!"
  onClick={handleClick}
  ref={fancyButtonRef}
/>;

Component Lifecycle Changes

React’s class component API has been around for years with little change. However, as we add support for more advanced features (such as error boundaries and the upcoming async rendering mode) we stretch this model in ways that it was not originally intended.

For example, with the current API, it is too easy to block the initial render with non-essential logic. In part this is because there are too many ways to accomplish a given task, and it can be unclear which is best. We’ve observed that the interrupting behavior of error handling is often not taken into consideration and can result in memory leaks (something that will also impact the upcoming async rendering mode). The current class component API also complicates other efforts, like our work on prototyping a React compiler.

Many of these issues are exacerbated by a subset of the component lifecycles (componentWillMount, componentWillReceiveProps, and componentWillUpdate). These also happen to be the lifecycles that cause the most confusion within the React community. For these reasons, we are going to deprecate those methods in favor of better alternatives.

We recognize that this change will impact many existing components. Because of this, the migration path will be as gradual as possible, and will provide escape hatches. (At Facebook, we maintain more than 50,000 React components. We depend on a gradual release cycle too!)

Note:

Deprecation warnings will be enabled with a future 16.x release, but the legacy lifecycles will continue to work until version 17.

Even in version 17, it will still be possible to use them, but they will be aliased with an “UNSAFE_” prefix to indicate that they might cause issues. We have also prepared an automated script to rename them in existing code.

In addition to deprecating unsafe lifecycles, we are also adding a couple of new lifecyles:

Learn more about these lifecycle changes here.

StrictMode Component

StrictMode is a tool for highlighting potential problems in an application. Like Fragment, StrictMode does not render any visible UI. It activates additional checks and warnings for its descendants.

Note:

StrictMode checks are run in development mode only; they do not impact the production build.

Although it is not possible for strict mode to catch all problems (e.g. certain types of mutation), it can help with many. If you see warnings in strict mode, those things will likely cause bugs for async rendering.

In version 16.3, StrictMode helps with:

  • Identifying components with unsafe lifecycles
  • Warning about legacy string ref API usage
  • Detecting unexpected side effects

Additional functionality will be added with future releases of React.

Learn more about the StrictMode component here.

React v16.4.0: Pointer Events

$
0
0

The latest minor release adds support for an oft-requested feature: pointer events!

It also includes a bugfix for getDerivedStateFromProps. Check out the full changelog below.

Pointer Events

The following event types are now available in React DOM:

  • onPointerDown
  • onPointerMove
  • onPointerUp
  • onPointerCancel
  • onGotPointerCapture
  • onLostPointerCapture
  • onPointerEnter
  • onPointerLeave
  • onPointerOver
  • onPointerOut

Please note that these events will only work in browsers that support the Pointer Events specification. (At the time of this writing, this includes the latest versions of Chrome, Firefox, Edge, and Internet Explorer.) If your application depends on pointer events, we recommend using a third-party pointer events polyfill. We have opted not to include such a polyfill in React DOM, to avoid an increase in bundle size.

Check out this example on CodeSandbox.

Huge thanks to Philipp Spiess for contributing this change!

Bugfix for getDerivedStateFromProps

getDerivedStateFromProps is now called every time a component is rendered, regardless of the cause of the update. Previously, it was only called if the component was re-rendered by its parent, and would not fire as the result of a local setState. This was an oversight in the initial implementation that has now been corrected. The previous behavior was more similar to componentWillReceiveProps, but the improved behavior ensures compatibility with React’s upcoming asynchronous rendering mode.

This bug fix will not affect most apps, but it may cause issues with a small fraction of components. The rare cases where it does matter fall into one of two categories:

1. Avoid Side Effects in getDerivedStateFromProps

Like the render method, getDerivedStateFromProps should be a pure function of props and state. Side effects in getDerivedStateFromProps were never supported, but since it now fires more often than it used to, the recent change may expose previously undiscovered bugs.

Side effectful code should be moved to other methods: for example, Flux dispatches typically belong inside the originating event handler, and manual DOM mutations belong inside componentDidMount or componentDidUpdate. You can read more about this in our recent post about preparing for asynchronous rendering.

2. Compare Incoming Props to Previous Props When Computing Controlled Values

The following code assumes getDerivedStateFromProps only fires on prop changes:

static getDerivedStateFromProps(props, state) {
  if (props.value !== state.controlledValue) {
    return {
      // Since this method fires on both props and state changes, local updates
      // to the controlled value will be ignored, because the props version
      // always overrides it. Oops!
      controlledValue: props.value,
    };
  }
  return null;
}

One possible way to fix this is to compare the incoming value to the previous value by storing the previous props in state:

static getDerivedStateFromProps(props, state) {
  const prevProps = state.prevProps;
  // Compare the incoming prop to previous prop
  const controlledValue =
    prevProps.value !== props.value
      ? props.value
      : state.controlledValue;
  return {
    // Store the previous props in state
    prevProps: props,
    controlledValue,
  };
}

However, try to avoid mixing props and state in this way — it’s rare that state needs to duplicate a value that exists in props and doing so can lead to subtle bugs. Prefer to have one source of truth for any value, and lift state up if necessary to share it between multiple components. Most uses of getDerivedStateFromProps (and its predecessor componentWillReceiveProps) can be fixed by moving the state management to a parent component.

Please remember that most components do not need getDerivedStateFromProps. It’s not meant to serve as a one-to-one replacement for componentWillReceiveProps. We’ll publish a follow-up post in the coming weeks with more recommendations on how to use (and not use) getDerivedStateFromProps.

Installation

React v16.4.0 is available on the npm registry.

To install React 16 with Yarn, run:

yarn add react@^16.4.0 react-dom@^16.4.0

To install React 16 with npm, run:

npm install --save react@^16.4.0 react-dom@^16.4.0

We also provide UMD builds of React via a CDN:

<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

Refer to the documentation for detailed installation instructions.

Changelog

React

React DOM

  • Add support for the Pointer Events specification. (@philipp-spiess in #12507)
  • Properly call getDerivedStateFromProps() regardless of the reason for re-rendering. (@acdlite in #12600 and #12802)
  • Fix a bug that prevented context propagation in some cases. (@gaearon in #12708)
  • Fix re-rendering of components using forwardRef() on a deeper setState(). (@gaearon in #12690)
  • Fix some attributes incorrectly getting removed from custom element nodes. (@airamrguez in #12702)
  • Fix context providers to not bail out on children if there’s a legacy context provider above. (@gaearon in #12586)
  • Add the ability to specify propTypes on a context provider component. (@nicolevy in #12658)
  • Fix a false positive warning when using react-lifecycles-compat in <StrictMode>. (@bvaughn in #12644)
  • Warn when the forwardRef() render function has propTypes or defaultProps. (@bvaughn in #12644)
  • Improve how forwardRef() and context consumers are displayed in the component stack. (@sophiebits in #12777)
  • Change internal event names. This can break third-party packages that rely on React internals in unsupported ways. (@philipp-spiess in #12629)

React Test Renderer

  • Fix the getDerivedStateFromProps() support to match the new React DOM behavior. (@koba04 in #12676)
  • Fix a testInstance.parent crash when the parent is a fragment or another special node. (@gaearon in #12813)
  • forwardRef() components are now discoverable by the test renderer traversal methods. (@gaearon in #12725)
  • Shallow renderer now ignores setState() updaters that return null or undefined. (@koba04 in #12756)

React ART

  • Fix reading context provided from the tree managed by React DOM. (@acdlite in #12779)

React Call Return (Experimental)

  • This experiment was deleted because it was affecting the bundle size and the API wasn’t good enough. It’s likely to come back in the future in some other form. (@gaearon in #12820)

React Reconciler (Experimental)

You Probably Don't Need Derived State

$
0
0

React 16.4 included a bugfix for getDerivedStateFromProps which caused some existing bugs in React components to reproduce more consistently. If this release exposed a case where your application was using an anti-pattern and didn’t work properly after the fix, we’re sorry for the churn. In this post, we will explain some common anti-patterns with derived state and our preferred alternatives.

For a long time, the lifecycle componentWillReceiveProps was the only way to update state in response to a change in props without an additional render. In version 16.3, we introduced a replacement lifecycle, getDerivedStateFromProps to solve the same use cases in a safer way. At the same time, we’ve realized that people have many misconceptions about how to use both methods, and we’ve found anti-patterns that result in subtle and confusing bugs. The getDerivedStateFromProps bugfix in 16.4 makes derived state more predictable, so the results of misusing it are easier to notice.

Note

All of the anti-patterns described in this post apply to both the older componentWillReceiveProps and the newer getDerivedStateFromProps.

This blog post will cover the following topics:

When to Use Derived State

getDerivedStateFromProps exists for only one purpose. It enables a component to update its internal state as the result of changes in props. Our previous blog post provided some examples, like recording the current scroll direction based on a changing offset prop or loading external data specified by a source prop.

We did not provide many examples, because as a general rule, derived state should be used sparingly. All problems with derived state that we have seen can be ultimately reduced to either (1) unconditionally updating state from props or (2) updating state whenever props and state don’t match. (We’ll go over both in more detail below.)

  • If you’re using derived state to memoize some computation based only on the current props, you don’t need derived state. See What about memoization? below.
  • If you’re updating derived state unconditionally or updating it whenever props and state don’t match, your component likely resets its state too frequently. Read on for more details.

Common Bugs When Using Derived State

The terms “controlled” and “uncontrolled” usually refer to form inputs, but they can also describe where any component’s data lives. Data passed in as props can be thought of as controlled (because the parent component controls that data). Data that exists only in internal state can be thought of as uncontrolled (because the parent can’t directly change it).

The most common mistake with derived state is mixing these two; when a derived state value is also updated by setState calls, there isn’t a single source of truth for the data. The external data loading example mentioned above may sound similar, but it’s different in a few important ways. In the loading example, there is a clear source of truth for both the “source” prop and the “loading” state. When the source prop changes, the loading state should always be overridden. Conversely, the state is overridden only when the prop changes and is otherwise managed by the component.

Problems arise when any of these constraints are changed. This typically comes in two forms. Let’s take a look at both.

Anti-pattern: Unconditionally copying props to state

A common misconception is that getDerivedStateFromProps and componentWillReceiveProps are only called when props “change”. These lifecycles are called any time a parent component rerenders, regardless of whether the props are “different” from before. Because of this, it has always been unsafe to unconditionally override state using either of these lifecycles. Doing so will cause state updates to be lost.

Let’s consider an example to demonstrate the problem. Here is a EmailInput component that “mirrors” an email prop in state:

class EmailInput extends Component {
  state = { email: this.props.email };

  render() {
    return <input onChange={this.handleChange} value={this.state.email} />;
  }

  handleChange = event => {
    this.setState({ email: event.target.value });
  };

  componentWillReceiveProps(nextProps) {
    // This will erase any local state updates!
    // Do not do this.
    this.setState({ email: nextProps.email });
  }
}

At first, this component might look okay. State is initialized to the value specified by props and updated when we type into the <input>. But if our component’s parent rerenders, anything we’ve typed into the <input> will be lost! (See this demo for an example.) This holds true even if we were to compare nextProps.email !== this.state.email before resetting.

In this simple example, adding shouldComponentUpdate to rerender only when the email prop has changed could fix this. However in practice, components usually accept multiple props; another prop changing would still cause a rerender and improper reset. Function and object props are also often created inline, making it hard to implement a shouldComponentUpdate that reliably returns true only when a material change has happened. Here is a demo that shows that happening. As a result, shouldComponentUpdate is best used as a performance optimization, not to ensure correctness of derived state.

Hopefully it’s clear by now why it is a bad idea to unconditionally copy props to state. Before reviewing possible solutions, let’s look at a related problematic pattern: what if we were to only update the state when the email prop changes?

Anti-pattern: Erasing state when props change

Continuing the example above, we could avoid accidentally erasing state by only updating it when props.email changes:

class EmailInput extends Component {
  state = {
    email: this.props.email
  };

  componentWillReceiveProps(nextProps) {
    // Any time props.email changes, update state.
    if (nextProps.email !== this.props.email) {
      this.setState({
        email: nextProps.email
      });
    }
  }
  
  // ...
}

Note

Even though the example above shows componentWillReceiveProps, the same anti-pattern applies to getDerivedStateFromProps.

We’ve just made a big improvement. Now our component will erase what we’ve typed only when the props actually change.

There is still a subtle problem. Imagine a password manager app using the above input component. When navigating between details for two accounts with the same email, the input would fail to reset. This is because the prop value passed to the component would be the same for both accounts! This would be a surprise to the user, as an unsaved change to one account would appear to affect other accounts that happened to share the same email. (See demo here.)

This design is fundamentally flawed, but it’s also an easy mistake to make. (I’ve made it myself!) Fortunately there are two alternatives that work better. The key to both is that for any piece of data, you need to pick a single component that owns it as the source of truth, and avoid duplicating it in other components. Let’s take a look at each of the alternatives.

Preferred Solutions

Recommendation: Fully controlled component

One way to avoid the problems mentioned above is to remove state from our component entirely. If the email address only exists as a prop, then we don’t have to worry about conflicts with state. We could even convert EmailInput to a lighter-weight functional component:

function EmailInput(props) {
  return <input onChange={props.onChange} value={props.email} />;
}

This approach simplifies the implementation of our component, but if we still want to store a draft value, the parent form component will now need to do that manually. (Click here to see a demo of this pattern.)

Recommendation: Fully uncontrolled component with a key

Another alternative would be for our component to fully own the “draft” email state. In that case, our component could still accept a prop for the initial value, but it would ignore subsequent changes to that prop:

class EmailInput extends Component {
  state = { email: this.props.defaultEmail };

  handleChange = event => {
    this.setState({ email: event.target.value });
  };

  render() {
    return <input onChange={this.handleChange} value={this.state.email} />;
  }
}

In order to reset the value when moving to a different item (as in our password manager scenario), we can use the special React attribute called key. When a key changes, React will create a new component instance rather than update the current one. Keys are usually used for dynamic lists but are also useful here. In our case, we could use the user ID to recreate the email input any time a new user is selected:

<EmailInput
  defaultEmail={this.props.user.email}
  key={this.props.user.id}
/>

Each time the ID changes, the EmailInput will be recreated and its state will be reset to the latest defaultEmail value. (Click here to see a demo of this pattern.) With this approach, you don’t have to add key to every input. It might make more sense to put a key on the whole form instead. Every time the key changes, all components within the form will be recreated with a freshly initialized state.

In most cases, this is the best way to handle state that needs to be reset.

Note

While this may sound slow, the performance difference is usually insignificant. Using a key can even be faster if the components have heavy logic that runs on updates since diffing gets bypassed for that subtree.

Alternative 1: Reset uncontrolled component with an ID prop

If key doesn’t work for some reason (perhaps the component is very expensive to initialize), a workable but cumbersome solution would be to watch for changes to “userID” in getDerivedStateFromProps:

class EmailInput extends Component {
  state = {
    email: this.props.defaultEmail,
    prevPropsUserID: this.props.userID
  };

  static getDerivedStateFromProps(props, state) {
    // Any time the current user changes,
    // Reset any parts of state that are tied to that user.
    // In this simple example, that's just the email.
    if (props.userID !== state.prevPropsUserID) {
      return {
        prevPropsUserID: props.userID,
        email: props.defaultEmail
      };
    }
    return null;
  }

  // ...
}

This also provides the flexibility to only reset parts of our component’s internal state if we so choose. (Click here to see a demo of this pattern.)

Note

Even though the example above shows getDerivedStateFromProps, the same technique can be used with componentWillReceiveProps.

Alternative 2: Reset uncontrolled component with an instance method

More rarely, you may need to reset state even if there’s no appropriate ID to use as key. One solution is to reset the key to a random value or autoincrementing number each time you want to reset. One other viable alternative is to expose an instance method to imperatively reset the internal state:

class EmailInput extends Component {
  state = {
    email: this.props.defaultEmail
  };

  resetEmailForNewUser(newEmail) {
    this.setState({ email: newEmail });
  }

  // ...
}

The parent form component could then use a ref to call this method. (Click here to see a demo of this pattern.)

Refs can be useful in certain cases like this one, but generally we recommend you use them sparingly. Even in the demo, this imperative method is nonideal because two renders will occur instead of one.


Recap

To recap, when designing a component, it is important to decide whether its data will be controlled or uncontrolled.

Instead of trying to “mirror” a prop value in state, make the component controlled, and consolidate the two diverging values in the state of some parent component. For example, rather than a child accepting a “committed” props.value and tracking a “draft” state.value, have the parent manage both state.draftValue and state.committedValue and control the child’s value directly. This makes the data flow more explicit and predictable.

For uncontrolled components, if you’re trying to reset state when a particular prop (usually an ID) changes, you have a few options:

  • Recomendation: To reset all internal state, use the key attribute.
  • Alternative 1: To reset only certain state fields, watch for changes in a special property (e.g. props.userID).
  • Alternative 2: You can also consider fall back to an imperative instance method using refs.

What about memoization?

We’ve also seen derived state used to ensure an expensive value used in render is recomputed only when the inputs change. This technique is known as memoization.

Using derived state for memoization isn’t necessarily bad, but it’s usually not the best solution. There is inherent complexity in managing derived state, and this complexity increases with each additional property. For example, if we add a second derived field to our component state then our implementation would need to separately track changes to both.

Let’s look at an example of one component that takes one prop—a list of items—and renders the items that match a search query entered by the user. We could use derived state to store the filtered list:

class Example extends Component {
  state = {
    filterText: "",
  };

  static getDerivedStateFromProps(props, state) {
    // Re-run the filter whenever the list array or filter text change.
    // Note we need to store prevPropsList and prevFilterText to detect changes.
    if (
      props.list !== state.prevPropsList ||
      state.prevFilterText !== state.filterText
    ) {
      return {
        prevPropsList: props.list,
        prevFilterText: state.filterText,
        filteredList: props.list.filter(item => item.text.includes(state.filterText))
      };
    }
    return null;
  }

  handleChange = event => {
    this.setState({ filterText: event.target.value });
  };

  render() {
    return (
      <Fragment>
        <input onChange={this.handleChange} value={this.state.filterText} />
        <ul>{this.state.filteredList.map(item => <li key={item.id}>{item.text}</li>)}</ul>
      </Fragment>
    );
  }
}

This implementation avoids recalculating filteredList more often than necessary. But it is more complicated than it needs to be, because it has to separately track and detect changes in both props and state in order to properly update the filtered list. In this example, we could simplify things by using PureComponent and moving the filter operation into the render method:

// PureComponents only rerender if at least one state or prop value changes.
// Change is determined by doing a shallow comparison of state and prop keys.
class Example extends PureComponent {
  // State only needs to hold the current filter text value:
  state = {
    filterText: ""
  };

  handleChange = event => {
    this.setState({ filterText: event.target.value });
  };

  render() {
    // The render method on this PureComponent is called only if
    // props.list or state.filterText has changed.
    const filteredList = this.props.list.filter(
      item => item.text.includes(this.state.filterText)
    )

    return (
      <Fragment>
        <input onChange={this.handleChange} value={this.state.filterText} />
        <ul>{filteredList.map(item => <li key={item.id}>{item.text}</li>)}</ul>
      </Fragment>
    );
  }
}

The above approach is much cleaner and simpler than the derived state version. Occasionally, this won’t be good enough—filtering may be slow for large lists, and PureComponent won’t prevent rerenders if another prop were to change. To address both of these concerns, we could add a memoization helper to avoid unnecessarily re-filtering our list:

import memoize from "memoize-one";

class Example extends Component {
  // State only needs to hold the current filter text value:
  state = { filterText: "" };

  // Re-run the filter whenever the list array or filter text changes:
  filter = memoize(
    (list, filterText) => list.filter(item => item.text.includes(filterText))
  );

  handleChange = event => {
    this.setState({ filterText: event.target.value });
  };

  render() {
    // Calculate the latest filtered list. If these arguments haven't changed
    // since the last render, `memoize-one` will reuse the last return value.
    const filteredList = this.filter(this.props.list, this.state.filterText);

    return (
      <Fragment>
        <input onChange={this.handleChange} value={this.state.filterText} />
        <ul>{filteredList.map(item => <li key={item.id}>{item.text}</li>)}</ul>
      </Fragment>
    );
  }
}

This is much simpler and performs just as well as the derived state version!

When using memoization, remember a couple of constraints:

  1. In most cases, you’ll want to attach the memoized function to a component instance. This prevents multiple instances of a component from resetting each other’s memoized keys.
  2. Typically you’ll want to use a memoization helper with a limited cache size in order to prevent memory leaks over time. (In the example above, we used memoize-one because it only caches the most recent argument and result.)
  3. None of the implementations shown in this section will work if props.items is recreated each time the parent component renders. But in most cases, this setup is appropriate.

In closing

In real world applications, components often contain a mix of controlled and uncontrolled behaviors. This is okay! If each value has a clear source of truth, you can avoid the anti-patterns mentioned above.

It is also worth re-iterating that getDerivedStateFromProps (and derived state in general) is an advanced feature and should be used sparingly because of this complexity. If your use case falls outside of these patterns, please share it with us on GitHub or Twitter!

React v16.4.2: Server-side vulnerability fix

$
0
0

We discovered a minor vulnerability that might affect some apps using ReactDOMServer. We are releasing a patch version for every affected React minor release so that you can upgrade with no friction. Read on for more details.

Short Description

Today, we are releasing a fix for a vulnerability we discovered in the react-dom/server implementation. It was introduced with the version 16.0.0 and has existed in all subsequent releases until today.

This vulnerability can only affect some server-rendered React apps. Purely client-rendered apps are not affected. Additionally, we expect that most server-rendered apps don’t contain the vulnerable pattern described below. Nevertheless, we recommend to follow the mitigation instructions at the earliest opportunity.

While we were investigating this vulnerability, we found similar vulnerabilities in a few other popular front-end libraries. We have coordinated this release together with Vue and Preact releases fixing the same issue. The tracking number for this vulnerability is CVE-2018-6341.

Mitigation

We have prepared a patch release with a fix for every affected minor version.

16.0.x

If you’re using react-dom/server with this version:

  • react-dom@16.0.0

Update to this version instead:

  • react-dom@16.0.1 (contains the mitigation)

16.1.x

If you’re using react-dom/server with one of these versions:

  • react-dom@16.1.0
  • react-dom@16.1.1

Update to this version instead:

  • react-dom@16.1.2 (contains the mitigation)

16.2.x

If you’re using react-dom/server with this version:

  • react-dom@16.2.0

Update to this version instead:

  • react-dom@16.2.1 (contains the mitigation)

16.3.x

If you’re using react-dom/server with one of these versions:

  • react-dom@16.3.0
  • react-dom@16.3.1
  • react-dom@16.3.2

Update to this version instead:

  • react-dom@16.3.3 (contains the mitigation)

16.4.x

If you’re using react-dom/server with one of these versions:

  • react-dom@16.4.0
  • react-dom@16.4.1

Update to this version instead:

  • react-dom@16.4.2 (contains the mitigation)

If you’re using a newer version of react-dom, no action is required.

Note that only the react-dom package needs to be updated.

Detailed Description

Your app might be affected by this vulnerability only if both of these two conditions are true:

  • Your app is being rendered to HTML using ReactDOMServer API, and
  • Your app includes a user-supplied attribute name in an HTML tag.

Specifically, the vulnerable pattern looks like this:

let props = {};
props[userProvidedData] = "hello";
let element = <div {...props} />;
let html = ReactDOMServer.renderToString(element);

In order to exploit it, the attacker would need to craft a special attribute name that triggers an XSS vulnerability. For example:

let userProvidedData = '></div><script>alert("hi")</script>';

In the vulnerable versions of react-dom/server, the output would let the attacker inject arbitrary markup:

<div ></div><script>alert("hi")</script>

In the versions after the vulnerability was fixed (and before it was introduced), attributes with invalid names are skipped:

<div></div>

You would also see a warning about an invalid attribute name.

Note that we expect attribute names based on user input to be very rare in practice. It doesn’t serve any common practical use case, and has other potential security implications that React can’t guard against.

Installation

React v16.4.2 is available on the npm registry.

To install React 16 with Yarn, run:

yarn add react@^16.4.2 react-dom@^16.4.2

To install React 16 with npm, run:

npm install --save react@^16.4.2 react-dom@^16.4.2

We also provide UMD builds of React via a CDN:

<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

Refer to the documentation for detailed installation instructions.

Changelog

React DOM Server

  • Fix a potential XSS vulnerability when the attacker controls an attribute name (CVE-2018-6341). This fix is available in the latest react-dom@16.4.2, as well as in previous affected minor versions: react-dom@16.0.1, react-dom@16.1.2, react-dom@16.2.1, and react-dom@16.3.3. (@gaearon in #13302)

  • Fix a crash in the server renderer when an attribute is called hasOwnProperty. This fix is only available in react-dom@16.4.2. (@gaearon in #13303)

Introducing the React Profiler

$
0
0

React 16.5 adds support for a new DevTools profiler plugin. This plugin uses React’s experimental Profiler API to collect timing information about each component that’s rendered in order to identify performance bottlenecks in React applications. It will be fully compatible with our upcoming time slicing and suspense features.

This blog post covers the following topics:

Profiling an application

DevTools will show a “Profiler” tab for applications that support the new profiling API:

New DevTools

Note:

react-dom 16.5+ supports profiling in DEV mode. A production profiling bundle is also available as react-dom/profiling. Read more about how to use this bundle at fb.me/react-profiling

The “Profiler” panel will be empty initially. Click the record button to start profiling:

Click

Once you’ve started recording, DevTools will automatically collect performance information each time your application renders. Use your app as you normally would. When you are finished profiling, click the “Stop” button.

Click

Assuming your application rendered at least once while profiling, DevTools will show several ways to view the performance data. We’ll take a look at each of these below.

Reading performace data

Browsing commits

Conceptually, React does work in two phases:

  • The render phase determines what changes need to be made to e.g. the DOM. During this phase, React calls render and then compares the result to the previous render.
  • The commit phase is when React applies any changes. (In the case of React DOM, this is when React inserts, updates, and removes DOM nodes.) React also calls lifecycles like componentDidMount and componentDidUpdate during this phase.

The DevTools profiler groups performance info by commit. Commits are displayed in a bar chart near the top of the profiler:

Bar chart of profiled commits

Each bar in the chart represents a single commit with the currently selected commit colored black. You can click on a bar (or the left/right arrow buttons) to select a different commit.

The color and height of each bar corresponds to how long that commit took to render. (Taller, yellow bars took longer than shorter, blue bars.)

Filtering commits

The longer you profile, the more times your application will render. In some cases you may end up with too many commits to easily process. The profiler offers a filtering mechanism to help with this. Use it to specify a threshold and the profiler will hide all commits that were faster than that value.

Filtering commits by time

Flame chart

The flame chart view represents the state of your application for a particular commit. Each bar in the chart represents a React component (e.g. App, Nav). The size and color of the bar represents how long it took to render the component and its children. (The width of a bar represents how much time was spent when the component last rendered and the color represents how much time was spent as part of the current commit.)

Example flame chart

Note:

The width of a bar indicates how long it took to render the component (and its children) when they last rendered. If the component did not re-render as part of this commit, the time represents a previous render. The wider a component is, the longer it took to render.

The color of a bar indicates how long the component (and its children) took to render in the selected commit. Yellow components took more time, blue components took less time, and gray components did not render at all during this commit.

For example, the commit shown above took a total of 18.4ms to render. The Router component was the “most expensive” to render (taking 18.4ms). Most of this time was due to two of its children, Nav (8.4ms) and Route (7.9ms). The rest of the time was divided between its remaining children or spent in the component’s own render method.

You can zoom in or out on a flame chart by clicking on components: Click on a component to zoom in or out

Clicking on a component will also select it and show information in the right side panel which includes its props and state at the time of this commit. You can drill into these to learn more about what the component actually rendered during the commit:

Viewing a component's props and state for a commit

In some cases, selecting a component and stepping between commits may also provide a hint as to why the component rendered:

Seeing which values changed between commits

The above image shows that state.scrollOffset changed between commits. This is likely what caused the List component to re-render.

Ranked chart

The ranked chart view represents a single commit. Each bar in the chart represents a React component (e.g. App, Nav). The chart is ordered so that the components which took the longest to render are at the top.

Example ranked chart

Note:

A component’s render time includes the time spent rendering its children, so the components which took the longest to render are generally near the top of the tree.

As with the flame chart, you can zoom in or out on a ranked chart by clicking on components.

Component chart

Sometimes it’s useful to see how many times a particular component rendered while you were profiling. The component chart provides this information in the form of a bar chart. Each bar in the chart represents a time when the component rendered. The color and height of each bar corresponds to how long the component took to render relative to other components in a particular commit.

Example component chart

The chart above shows that the List component rendered 11 times. It also shows that each time it rendered, it was the most “expensive” component in the commit (meaning that it took the longest).

To view this chart, either double-click on a component or select a component and click on the blue bar chart icon in the right detail pane. You can return to the previous chart by clicking the “x” button in the right detail pane. You can aso double click on a particular bar to view more information about that commit.

How to view all renders for a specific component

If the selected component did not render at all during the profiling session, the following message will be shown:

No render times for the selected component

Interactions

React recently added another experimental API for tracking the cause of an update. “Interactions” tracked with this API will also be shown in the profiler:

The interactions panel

The image above shows a profiling session that tracked four interactions. Each row represents an interaction that was tracked. The colored dots along the row represent commits that were related to that interaction.

You can also see which interactions were tracked for a particular commit from the flame chart and ranked chart views as well:

List of interactions for a commit

You can navigate between interactions and commits by clicking on them:

Navigate between interactions and commits

The tracking API is still new and we will cover it in more detail in a future blog post.

Troubleshooting

No profiling data has been recorded for the selected root

If your your application has multiple “roots”, you may see the following message after profiling: No profiling data has been recorded for the selected root

This message indicates that no performance data was recorded for the root that’s selected in the “Elements” panel. In this case, try selecting a different root in that panel to view profiling information for that root:

Select a root in the "Elements" panel to view its performance data

No timing data to display for the selected commit

Sometimes a commit may be so fast that performance.now() doesn’t give DevTools any meaningful timing information. In this case, the following message will be shown:

No timing data to display for the selected commit

Create React App 2.0: Babel 7, Sass, and More

$
0
0

Create React App 2.0 has been released today, and it brings a year’s worth of improvements in a single dependency update.

While React itself doesn’t require any build dependencies, it can be challenging to write a complex app without a fast test runner, a production minifier, and a modular codebase. Since the very first release, the goal of Create React App has been to help you focus on what matters the most — your application code — and to handle build and testing setup for you.

Many of the tools it relies on have since released new versions containing new features and performance improvements: Babel 7, webpack 4, and Jest 23. However, updating them manually and making them work well together takes a lot of effort. And this is exactly what Create React App 2.0 contributors have been busy with for the past few months: migrating the configuration and dependencies so that you don’t need to do it yourself.

Now that Create React App 2.0 is out of beta, let’s see what’s new and how you can try it!

Note

Don’t feel pressured to upgrade anything. If you’re satisfied with the current feature set, its performance, and reliability, you can keep using the version you’re currently at! It might also be a good idea to let the 2.0 release stabilize a little bit before switching to it in production.

What’s New

Here’s a short summary of what’s new in this release:

All of these features work out of the box — to enable them, follow the below instructions.

Starting a Project with Create React App 2.0

You don’t need to update anything special. Starting from today, when you run create-react-app it will use the 2.0 version of the template by default. Have fun!

If you want to use the old 1.x template for some reason, you can do that by passing --scripts-version=react-scripts@1.x as an argument to create-react-app.

Updating a Project to Create React App 2.0

Upgrading a non-ejected project to Create React App 2.0 should usually be straightforward. Open package.json in the root of your project and find react-scripts there.

Then change its version to 2.0.3:

  // ... other dependencies ...
  "react-scripts": "2.0.3"

Run npm install (or yarn, if you use it). For many projects, this one-line change is sufficient to upgrade!

Here’s a few more tips to get you started.

When you run npm start for the first time after the upgrade, you’ll get a prompt asking about which browsers you’d like to support. Press y to accept the default ones. They’ll be written to your package.json and you can edit them any time. Create React App will use this information to produce smaller or polyfilled CSS bundles depending on whether you target modern browsers or older browsers.

If npm start still doesn’t quite work for you after the upgrade, check out the more detailed migration instructions in the release notes. There are a few breaking changes in this release but the scope of them is limited, so they shouldn’t take more than a few hours to sort out. Note that support for older browsers is now opt-in to reduce the polyfill size.

If you previously ejected but now want to upgrade, one common solution is to find the commits where you ejected (and any subsequent commits changing the configuration), revert them, upgrade, and later optionally eject again. It’s also possible that the feature you ejected for (maybe Sass or CSS Modules?) is now supported out of the box.

Note

Due to a possible bug in npm, you might see warnings about unsatisfied peer dependencies. You should be able to ignore them. As far as we’re aware, this issue isn’t present with Yarn.

Breaking Changes

Here’s a short list of breaking changes in this release:

  • Node 6 is no longer supported.
  • Support for older browsers (such as IE 9 to IE 11) is now opt-in with a separate package.
  • Code-splitting with import() now behaves closer to specification, while require.ensure() is disabled.
  • The default Jest environment now includes jsdom.
  • Support for specifying an object as proxy setting was replaced with support for a custom proxy module.
  • Support for .mjs extension was removed until the ecosystem around it stabilizes.
  • PropTypes definitions are automatically stripped out of the production builds.

If either of these points affects you, 2.0.3 release notes contain more detailed instructions.

Learn More

You can find the full changelog in the release notes. This was a large release, and we may have missed something. Please report any problems to our issue tracker and we’ll try to help.

Note

If you’ve been using 2.x alpha versions, we provide separate migration instructions for them.

Thanks

This release wouldn’t be possible without our wonderful community of contributors. We’d like to thank Andreas Cederström, Clement Hoang, Brian Ng, Kent C. Dodds, Ade Viankakrisna Fadlil, Andrey Sitnik, Ro Savage, Fabiano Brito, Ian Sutherland, Pete Nykänen, Jeffrey Posnick, Jack Zhao, Tobias Koppers, Henry Zhu, Maël Nison, XiaoYan Li, Marko Trebizan, Marek Suscak, Mikhail Osher, and many others who provided feedback and testing for this release.

React v16.6.0: lazy, memo and contextType

$
0
0

Today we’re releasing React 16.6 with a few new convenient features. A form of PureComponent/shouldComponentUpdate for function components, a way to do code splitting using Suspense and an easier way to consume Context from class components.

Check out the full changelog below.

React.memo

Class components can bail out from rendering when their input props are the same using PureComponent or shouldComponentUpdate. Now you can do the same with function components by wrapping them in React.memo.

const MyComponent = React.memo(function MyComponent(props) {
  /* only rerenders if props change */
});

React.lazy: Code-Splitting with Suspense

You may have seen Dan’s talk about React Suspense at JSConf Iceland. Now you can use the Suspense component to do code-splitting by wrapping a dynamic import in a call to React.lazy().

import React, {lazy, Suspense} from 'react';
const OtherComponent = lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <OtherComponent />
    </Suspense>
  );
}

The Suspense component will also allow library authors to start building data fetching with Suspense support in the future.

Note: This feature is not yet available for server-side rendering. Suspense support will be added in a later release.

static contextType

In React 16.3 we introduced the official Context API as a replacement to the previous Legacy Context API.

const MyContext = React.createContext();

We’ve heard feedback that adopting the new render prop API can be difficult in class components. So we’ve add a convenience API to consume a context value from within a class component.

class MyClass extends React.Component {
  static contextType = MyContext;
  componentDidMount() {
    let value = this.context;
    /* perform a side-effect at mount using the value of MyContext */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* render something based on the value of MyContext */
  }
}

static getDerivedStateFromError()

React 16 introduced Error Boundaries for handling errors thrown in React renders. We already had the componentDidCatch lifecycle method which gets fired after an error has already happened. It’s great for logging errors to the server. It also lets you show a different UI to the user by calling setState.

Before that is fired, we render null in place of the tree that threw an error. This sometimes breaks parent components that don’t expect their refs to be empty. It also doesn’t work to recover from errors on the server since the Did lifecycle methods don’t fire during server-side rendering.

We’re adding another error method that lets you render the fallback UI before the render completes. See the docs for getDerivedStateFromError().

Note: getDerivedStateFromError() is not yet available for server-side rendering. It is designed to work with server-side rendering in a future release. We’re releasing it early so that you can start preparing to use it.

Deprecations in StrictMode

In 16.3 we introduced the StrictMode component. It lets you opt-in to early warnings for patterns that might cause problems in the future.

We’ve added two more APIs to the list of deprecated APIs in StrictMode. If you don’t use StrictMode you don’t have to worry; these warning won’t fire for you.

  • ReactDOM.findDOMNode() - This API is often misunderstood and most uses of it are unnecessary. It can also be surprisingly slow in React 16. See the docs for possible upgrade paths.
  • Legacy Context using contextTypes and getChildContext - Legacy context makes React slightly slower and bigger than it needs to be. That’s why we strongly want to encourage upgrading to the new context API. Hopefully the addition of the contextType API makes this a bit easier.

If you’re having trouble upgrading, we’d like to hear your feedback.

Installation

React v16.6.0 is available on the npm registry.

To install React 16 with Yarn, run:

yarn add react@^16.6.0 react-dom@^16.6.0

To install React 16 with npm, run:

npm install --save react@^16.6.0 react-dom@^16.6.0

We also provide UMD builds of React via a CDN:

<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

Refer to the documentation for detailed installation instructions.

Changelog

React

React DOM

  • Add contextType as a more ergonomic way to subscribe to context from a class. (@bvaughn in #13728)
  • Add getDerivedStateFromError lifecycle method for catching errors in a future asynchronous server-side renderer. (@bvaughn in #13746)
  • Warn when <Context> is used instead of <Context.Consumer>. (@trueadm in #13829)
  • Fix gray overlay on iOS Safari. (@philipp-spiess in #13778)
  • Fix a bug caused by overwriting window.event in development. (@sergei-startsev in #13697)

React DOM Server

Scheduler (Experimental)


React Conf recap: Hooks, Suspense, and Concurrent Rendering

$
0
0

This year’s React Conf took place on October 25 and 26 in Henderson, Nevada, where more than 600 attendees gathered to discuss the latest in UI engineering.


Sophie Alpert and Dan Abramov kicked off Day 1 with their keynote, React Today and Tomorrow. In the talk, they introduced Hooks, which are a new proposal that adds the ability to access features such as state without writing a JavaScript class. Hooks promise to dramatically simplify the code required for React components and are currently available in a React alpha release.


On the morning of Day 2, Andrew Clark and Brian Vaughn presented Concurrent Rendering in React. Andrew covered the recently announced React.lazy API for code splitting and previewed two upcoming features: concurrent mode and Suspense. Brian demonstrated how to use React’s new profiler tooling to make apps built in React run faster.


In the afternoon, Parashuram N spoke in detail about React Native’s New Architecture, a long-term project that the React Native team has been working on over the past year and announced in June. We’re really excited about the potential of this project to improve performance, simplify interoperability with other libraries, and set a strong foundation for the future of React Native.

Now that the conference is over, all 28 conference talks are available to stream online. There are tons of great ones from both days. We can’t wait until next year!

React 16.x Roadmap

$
0
0

You might have heard about features like “Hooks”, “Suspense”, and “Concurrent Rendering” in the previous blog posts and talks. In this post, we’ll look at how they fit together and the expected timeline for their availability in a stable release of React.

tl;dr

We plan to split the rollout of new React features into the following milestones:

These are estimates, and the details may change as we’re further along. There’s at least two more projects we plan to complete in 2019. They require more exploration and aren’t tied to a particular release yet:

We expect to get more clarity on their timeline in the coming months.

Note

This post is just a roadmap — there is nothing in it that requires your immediate attention. When each of these features are released, we’ll publish a full blog post announcing them.

Release Timeline

We have a single vision for how all of these features fit together, but we’re releasing each part as soon as it is ready so that you can test and start using them sooner. The API design doesn’t always make sense when looking at once piece in isolation; this post lays out the major parts of our plan to help you see the whole picture. (See our versioning policy to learn more about our commitment to stability.)

The gradual release strategy helps us refine the APIs, but the transitional period when some things aren’t ready can be confusing. Let’s look at what these different features mean for your app, how they relate to each other, and when you can expect to start learning and using them.

React 16.6: Suspense for Code Splitting (shipped)

Suspense refers to React’s new ability to “suspend” rendering while components are waiting for something, and display a loading indicator. In React 16.6, Suspense supports only one use case: lazy loading components with React.lazy() and <React.Suspense>.

// This component is loaded dynamically
const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <React.Suspense fallback={<Spinner />}>
      <div>
        <OtherComponent />
      </div>
    </React.Suspense>
  );
}

Code splitting with React.lazy() with <React.Suspense> is documented in the Code Splitting guide. You can find another practical explanation in this article.

We have been using Suspense for code splitting at Facebook since July, and expect it to be stable. There’s been a few regressions in the initial public release in 16.6.0, but they were fixed in 16.6.3.

Code splitting is just the first step for Suspense. Our longer term vision for Suspense involves letting it handle data fetching too (and integrate with libraries like Apollo). In addition to a convenient programming model, Suspense also provides better user experience in Concurrent Mode. You’ll find information about these topics further below.

Status in React DOM: Available since React 16.6.0.

Status in React DOM Server: Suspense is not available in the server renderer yet. This isn’t for the lack of attention. We’ve started work on a new asynchronous server renderer that will support Suspense, but it’s a large project and will take a good chunk of 2019 to complete.

Status in React Native: Bundle splitting isn’t very useful in React Native, but there’s nothing technically preventing React.lazy() and <Suspense> from working when given a Promise to a module.

Recommendation: If you only do client rendering, we recommend widely adopting React.lazy() and <React.Suspense> for code splitting React components. If you do server rendering, you’ll have to wait with adoption until the new server renderer is ready.

React 16.7: Hooks (~Q1 2019)

Hooks let you use features like state and lifecycle from function components. They also let you reuse stateful logic between components without introducing extra nesting in your tree.

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
   <div>
     <p>You clicked {count} times</p>
     <button onClick={() => setCount(count + 1)}>
       Click me
     </button>
   </div>
 );
}

Hooks introduction and overview are good places to start. Watch these talks for a video introduction and a deep dive. The FAQ should answer most of your further questions. To learn more about the motivation behind Hooks, you can read this article. Some of the rationale for the API design of Hooks is explained in this RFC thread reply.

We have been dogfooding Hooks at Facebook since September. We don’t expect major bugs in the implementation. Hooks are only available in the 16.7 alpha versions of React. Some of their API is expected to change in the final 16.7 version (see the end of this comment for details).

Hooks represent our vision for the future of React. They solve both problems that React users experience directly (“wrapper hell” of render props and higher-order components, duplication of logic in lifecycle methods), and the issues we’ve encountered optimizing React at scale (such as difficulties in inlining components with a compiler). Hooks don’t deprecate classes. However, if Hooks are successful, it is possible that in a future major release class support might move to a separate package, reducing the default bundle size of React.

Status in React DOM: The first version of react and react-dom supporting Hooks is 16.7.0-alpha.0. We expect to publish more alphas over the next months (at the time of writing, the latest one is 16.7.0-alpha.2). You can try them by installing react@next with react-dom@next. Don’t forget to update react-dom — otherwise Hooks won’t work.

Status in React DOM Server: The same 16.7 alpha versions of react-dom fully support Hooks with react-dom/server.

Status in React Native: There is no officially supported way to try Hooks in React Native yet. If you’re feeling adventurous, check out this thread for unofficial instructions. There is a known issue with useEffect firing too late which still needs to be solved.

Recommendation: When you’re ready, we encourage you to start trying Hooks in new components you write. Make sure everyone on your team is on board with using them and familiar with this documentation. We don’t recommend rewriting your existing classes to Hooks unless you planned to rewrite them anyway (e.g. to fix bugs). Read more about the adoption strategy here.

React 16.8: Concurrent Mode (~Q2 2019)

Concurrent Mode lets React apps be more responsive by rendering component trees without blocking the main thread. It is opt-in and allows React to interrupt a long-running render (for example, rendering a new feed story) to handle a high-priority event (for example, text input or hover). Concurrent Mode also improves the user experience of Suspense by skipping unnecessary loading states on fast connections.

Note

You might have previously heard Concurrent Mode being referred to as “async mode”. We’ve changed the name to Concurrent Mode to highlight React’s ability to perform work on different priority levels. This sets it apart from other approaches to async rendering.

// Two ways to opt in:

// 1. Part of an app (not final API)
<React.unstable_ConcurrentMode>
  <Something />
</React.unstable_ConcurrentMode>

// 2. Whole app (not final API)
ReactDOM.unstable_createRoot(domNode).render(<App />);

There is no documentation written for the Concurrent Mode yet. It is important to highlight that the conceptual model will likely be unfamiliar at first. Documenting its benefits, how to use it efficiently, and its pitfalls is a high priority for us, and will be a prerequisite for calling it stable. Until then, Andrew’s talk is the best introduction available.

Concurrent Mode is much less polished than Hooks. Some APIs aren’t properly “wired up” yet and don’t do what they’re expected to. At the time of writing this post, we don’t recommend using it for anything except very early experimentation. We don’t expect many bugs in Concurrent Mode itself, but note that components that produce warnings in <React.StrictMode> may not work correctly. On a separate note, we’ve seen that Concurrent Mode surfaces performance problems in other code which can sometimes be mistaken for performance issues in Concurrent Mode itself. For example, a stray setInterval(fn, 1) call that runs every millisecond would have a worse effect in Concurrent Mode. We plan to publish more guidance about diagnosing and fixing issues like this as part of the 16.8 release documentation.

Concurrent Mode is a big part of our vision for React. For CPU-bound work, it allows non-blocking rendering and keeps your app responsive while rendering complex component trees. That’s demoed in the first part of our JSConf Iceland talk. Concurrent Mode also makes Suspense better. It lets you avoid flickering a loading indicator if the network is fast enough. It’s hard to explain without seeing so Andrew’s talk is the best resource available today. Concurrent Mode relies on a cooperative main thread scheduler, and we are collaborating with the Chrome team to eventually move this functionality into the browser itself.

Status in React DOM: A very unstable version of Concurrent Mode is available behind an unstable_ prefix in React 16.6 but we don’t recommend trying it unless you’re willing to often run into walls or missing features. The 16.7 alphas include React.ConcurrentMode and ReactDOM.createRoot without an unstable_ prefix, but we’ll likely keep the prefix in 16.7, and only document and mark Concurrent Mode as stable in React 16.8.

Status in React DOM Server: Concurrent Mode doesn’t directly affect server rendering. It will work with the existing server renderer.

Status in React Native: The current plan is to delay enabling Concurrent Mode in React Native until React Fabric project is near completion.

Recommendation: If you wish to adopt Concurrent Mode in the future, wrapping some component subtrees in <React.StrictMode> and fixing the resulting warnings is a good first step. In general it’s not expected that legacy code would immediately be compatible. For example, at Facebook we mostly intend to use the Concurrent Mode in the more recently developed codebases, and keep the legacy ones running in the synchronous mode for the near future.

React 16.9: Suspense for Data Fetching (~mid 2019)

As mentioned earlier, Suspense refers to React’s ability to “suspend” rendering while components are waiting for something, and display a loading indicator. In the already shipped React 16.6, the only supported use case for Suspense is code splitting. In the future 16.9 release, we’d like to provide officially supported ways to use it for data fetching too. We’ll provide a reference implementation of a basic “React Cache” that’s compatible with Suspense, but you can also write your own. Data fetching libraries like Apollo and Relay will be able to integrate with Suspense by following a simple specification that we’ll document.

// React Cache for simple data fetching (not final API)
import {unstable_createResource} from 'react-cache';

// Tell React Cache how to fetch your data
const TodoResource = unstable_createResource(fetchTodo);

function Todo(props) {
  // Suspends until the data is in the cache
  const todo = TodoResource.read(props.id);
  return <li>{todo.title}</li>;
}

function App() {
  return (
    // Same Suspense component you already use for code splitting
    // would be able to handle data fetching too.
    <React.Suspense fallback={<Spinner />}>
      <ul>
        {/* Siblings fetch in parallel */}
        <Todo id="1" />
        <Todo id="2" />
      </ul>
    </React.Suspense>
  );
}

// Other libraries like Apollo and Relay can also
// provide Suspense integrations with similar APIs.

There is no official documentation for how to fetch data with Suspense yet, but you can find some early information in this talk and this small demo. We’ll write documentation for React Cache (and how to write your own Suspense-compatible library) closer to the React 16.9 release, but if you’re curious, you can find its very early source code here.

The low-level Suspense mechanism (suspending rendering and showing a fallback) is expected to be stable even in React 16.6. We’ve used it for code splitting in production for months. However, the higher-level APIs for data fetching are very unstable. React Cache is rapidly changing, and will change at least a few more times. There are some low-level APIs that are missing for a good higher-level API to be possible. We don’t recommend using React Cache anywhere except very early experiments. Note that React Cache itself isn’t strictly tied to React releases, but the current alphas lack basic features as cache invalidation, and you’ll run into a wall very soon. We expect to have something usable with the React 16.9 release.

Eventually we’d like most data fetching to happen through Suspense but it will take a long time until all integrations are ready. In practice we expect it to be adopted very incrementally, and often through layers like Apollo or Relay rather than directly. Missing higher level APIs aren’t the only obstacle — there are also some important UI patterns we don’t support yet such as showing progress indicator outside of the loading view hierarchy. As always, we will communicate our progress in the release notes on this blog.

Status in React DOM and React Native: Technically, a compatible cache would already work with <React.Suspense> in React 16.6. However, we don’t expect to have a good cache implementation until React 16.9. If you’re feeling adventurous, you can try to write your own cache by looking at the React Cache alphas. However, note that the mental model is sufficiently different that there’s a high risk of misunderstanding it until the docs are ready.

Status in React DOM Server: Suspense is not available in the server renderer yet. As we mentioned earlier, we’ve started work on a new asynchronous server renderer that will support Suspense, but it’s a large project and will take a good chunk of 2019 to complete.

Recommendation: Wait for 16.9 in order to use Suspense for data fetching. Don’t try to use Suspense features in 16.6 for it; it’s not supported. However, your existing <Suspense> components for code splitting will be able to show loading states for data too when Suspense for Data Fetching becomes officially supported.

Other Projects

Modernizing React DOM

We started an investigation into simplifying and modernizing ReactDOM, with a goal of reduced bundle size and aligning closer with the browser behavior. It is still early to say which specific bullet points will “make it” because the project is in an exploratory phase. We will communicate our progress on that issue.

Suspense for Server Rendering

We started designing a new server renderer that supports Suspense (including waiting for asynchronous data on the server without double rendering) and progressively loading and hydrating page content in chunks for best user experience. You can watch an overview of its early prototype in this talk. The new server renderer is going to be our major focus in 2019, but it’s too early to say anything about its release schedule. Its development, as always, will happen on GitHub.


And that’s about it! As you can see, there’s a lot here to keep us busy but we expect much progress in the coming months.

We hope this post gives you an idea of what we’re working on, what you can use today, and what you can expect to use in the future. While there’s a lot of discussion about new features on social media platforms, you won’t miss anything important if you read this blog.

We’re always open to feedback, and love to hear from you in the RFC repository, the issue tracker, and on Twitter.

React v16.7: No, This Is Not The One With Hooks

$
0
0

Our latest release includes an important performance bugfix for React.lazy. Although there are no API changes, we’re releasing it as a minor instead of a patch.

Why Is This Bugfix a Minor Instead of a Patch?

React follows semantic versioning. Typically, this means that we use patch versions for bugfixes, and minors for new (non-breaking) features. However, we reserve the option to release minor versions even if they do not include new features. The motivation is to reserve patches for changes that have a very low chance of breaking. Patches are the most important type of release because they sometimes contain critical bugfixes. That means patch releases have a higher bar for reliability. It’s unacceptable for a patch to introduce additional bugs, because if people come to distrust patches, it compromises our ability to fix critical bugs when they arise — for example, to fix a security vulnerability.

We never intend to ship bugs. React has a hard-earned reputation for stability, and we intend to keep it that way. We thoroughly test every version of React before releasing. This includes unit tests, generative (fuzzy) tests, integration tests, and internal dogfooding across tens of thousands of components. However, sometimes we make mistakes. That’s why, going forward, our policy will be that if a release contains non-trivial changes, we will bump the minor version, even if the external behavior is the same. We’ll also bump the minor when changing unstable_-prefixed APIs.

Can I Use Hooks Yet?

Not yet, but soon!

At React Conf, we said that 16.7 would be the first release to include Hooks. This was a mistake. We shouldn’t have attached a specific version number to an unreleased feature. We’ll avoid this in the future.

Although 16.7 does not include Hooks, please do not infer anything about the timeline of the Hooks launch. Our plans for Hooks are unchanged:

We’ve heard from many people who want to start using Hooks in their apps. We also can’t wait to ship them! But because Hooks changes how we write React components, we are taking extra time to get the details right. We appreciate your patience as we prepare this exciting new feature for widespread, ahem, use.

Learn more about our roadmap in our previous post.

Installation

React v16.7.0 is available on the npm registry.

To install React 16 with Yarn, run:

yarn add react@^16.7.0 react-dom@^16.7.0

To install React 16 with npm, run:

npm install --save react@^16.7.0 react-dom@^16.7.0

We also provide UMD builds of React via a CDN:

<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

Refer to the documentation for detailed installation instructions.

Changelog

React DOM

  • Fix performance of React.lazy for large numbers of lazily-loaded components. (@acdlite in #14429)
  • Clear fields on unmount to avoid memory leaks. (@trueadm in #14276)
  • Fix bug with SSR and context when mixing react-dom/server@16.6 and react@<16.6. (@gaearon in #14291)
  • Fix a performance regression in profiling mode. (@bvaughn in #14383)

Scheduler (Experimental)

React v16.8: The One With Hooks

$
0
0

With React 16.8, React Hooks are available in a stable release!

What Are Hooks?

Hooks let you use state and other React features without writing a class. You can also build your own Hooks to share reusable stateful logic between components.

If you’ve never heard of Hooks before, you might find these resources interesting:

You don’t have to learn Hooks right now. Hooks have no breaking changes, and we have no plans to remove classes from React. The Hooks FAQ describes the gradual adoption strategy.

No Big Rewrites

We don’t recommend rewriting your existing applications to use Hooks overnight. Instead, try using Hooks in some of the new components, and let us know what you think. Code using Hooks will work side by side with existing code using classes.

Can I Use Hooks Today?

Yes! Starting with 16.8.0, React includes a stable implementation of React Hooks for:

  • React DOM
  • React DOM Server
  • React Test Renderer
  • React Shallow Renderer

Note that to enable Hooks, all React packages need to be 16.8.0 or higher. Hooks won’t work if you forget to update, for example, React DOM.

React Native will support Hooks in the 0.60 release.

Tooling Support

React Hooks are now supported by React DevTools. They are also supported in the latest Flow and TypeScript definitions for React. We strongly recommend enabling a new lint rule called eslint-plugin-react-hooks to enforce best practices with Hooks. It will soon be included into Create React App by default.

What’s Next

We described our plan for the next months in the recently published React Roadmap.

Note that React Hooks don’t cover all use cases for classes yet but they’re very close. Currently, only getSnapshotBeforeUpdate() and componentDidCatch() methods don’t have equivalent Hooks APIs, and these lifecycles are relatively uncommon. If you want, you should be able to use Hooks in most of the new code you’re writing.

Even while Hooks were in alpha, the React community created many interesting examples and recipes using Hooks for animations, forms, subscriptions, integrating with other libraries, and so on. We’re excited about Hooks because they make code reuse easier, helping you write your components in a simpler way and make great user experiences. We can’t wait to see what you’ll create next!

Testing Hooks

We have added a new API called ReactTestUtils.act() in this release. It ensures that the behavior in your tests matches what happens in the browser more closely. We recommend to wrap any code rendering and triggering updates to your components into act() calls. Testing libraries like react-testing-library can also wrap their APIs with it.

For example, the counter example from this page can be tested like this:

import React from 'react';
import ReactDOM from 'react-dom';
import { act } from 'react-dom/test-utils';
import Counter from './Counter';

let container;

beforeEach(() => {
  container = document.createElement('div');
  document.body.appendChild(container);
});

afterEach(() => {
  document.body.removeChild(container);
  container = null;
});

it('can render and update a counter', () => {
  // Test first render and effect
  act(() => {
    ReactDOM.render(<Counter />, container);
  });
  const button = container.querySelector('button');
  const label = container.querySelector('p');
  expect(label.textContent).toBe('You clicked 0 times');
  expect(document.title).toBe('You clicked 0 times');

  // Test second render and effect
  act(() => {
    button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
  });
  expect(label.textContent).toBe('You clicked 1 times');
  expect(document.title).toBe('You clicked 1 times');
});

The calls to act() will also flush the effects inside of them.

If you need to test a custom Hook, you can do so by creating a component in your test, and using your Hook from it. Then you can test the component you wrote.

To reduce the boilerplate, we recommend using react-testing-library which is designed to encourage writing tests that use your components as the end users do.

Thanks

We’d like to thank everybody who commented on the Hooks RFC for sharing their feedback. We’ve read all of your comments and made some adjustments to the final API based on them.

Installation

React

React v16.8.0 is available on the npm registry.

To install React 16 with Yarn, run:

yarn add react@^16.8.0 react-dom@^16.8.0

To install React 16 with npm, run:

npm install --save react@^16.8.0 react-dom@^16.8.0

We also provide UMD builds of React via a CDN:

<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

Refer to the documentation for detailed installation instructions.

ESLint Plugin for React Hooks

Note

As mentioned above, we strongly recommend using the eslint-plugin-react-hooks lint rule.

If you’re using Create React App, instead of manually configuring ESLint you can wait for the next version of react-scripts which will come out shortly and will include this rule.

Assuming you already have ESLint installed, run:

# npm
npm install eslint-plugin-react-hooks@next --save-dev

# yarn
yarn add eslint-plugin-react-hooks@next --dev

Then add it to your ESLint configuration:

{
  "plugins": [
    // ...
    "react-hooks"
  ],
  "rules": {
    // ...
    "react-hooks/rules-of-hooks": "error"
  }
}

Changelog

React

  • Add Hooks — a way to use state and other React features without writing a class. (@acdlite et al. in #13968)
  • Improve the useReducer Hook lazy initialization API. (@acdlite in #14723)

React DOM

  • Bail out of rendering on identical values for useState and useReducer Hooks. (@acdlite in #14569)
  • Don’t compare the first argument passed to useEffect/useMemo/useCallback Hooks. (@acdlite in #14594)
  • Use Object.is algorithm for comparing useState and useReducer values. (@Jessidhia in #14752)
  • Support synchronous thenables passed to React.lazy(). (@gaearon in #14626)
  • Render components with Hooks twice in Strict Mode (DEV-only) to match class behavior. (@gaearon in #14654)
  • Warn about mismatching Hook order in development. (@threepointone in #14585 and @acdlite in #14591)
  • Effect clean-up functions must return either undefined or a function. All other values, including null, are not allowed. @acdlite in #14119

React Test Renderer

  • Support Hooks in the shallow renderer. (@trueadm in #14567)
  • Fix wrong state in shouldComponentUpdate in the presence of getDerivedStateFromProps for Shallow Renderer. (@chenesan in #14613)
  • Add ReactTestRenderer.act() and ReactTestUtils.act() for batching updates so that tests more closely match real behavior. (@threepointone in #14744)

ESLint Plugin: React Hooks

Hooks Changelog Since Alpha Versions

The above changelog contains all notable changes since our last stable release (16.7.0). As with all our minor releases, none of the changes break backwards compatibility.

If you’re currently using Hooks from an alpha build of React, note that this release does contain some small breaking changes to Hooks. We don’t recommend depending on alphas in production code. We publish them so we can make changes in response to community feedback before the API is stable.

Here are all breaking changes to Hooks that have been made since the first alpha release:

  • Remove useMutationEffect. (@sophiebits in #14336)
  • Rename useImperativeMethods to useImperativeHandle. (@threepointone in #14565)
  • Bail out of rendering on identical values for useState and useReducer Hooks. (@acdlite in #14569)
  • Don’t compare the first argument passed to useEffect/useMemo/useCallback Hooks. (@acdlite in #14594)
  • Use Object.is algorithm for comparing useState and useReducer values. (@Jessidhia in #14752)
  • Render components with Hooks twice in Strict Mode (DEV-only). (@gaearon in #14654)
  • Improve the useReducer Hook lazy initialization API. (@acdlite in #14723)

Is React Translated Yet? ¡Sí! Sim! はい!

$
0
0

We’re excited to announce an ongoing effort to maintain official translations of the React documentation website into different languages. Thanks to the dedicated efforts of React community members from around the world, React is now being translated into over 30 languages! You can find them on the new Languages page.

In addition, the following three languages have completed translating most of the React Docs! 🎉

Special congratulations to Alejandro Ñáñez Ortiz, Rainer Martínez Fraga, David Morales, Miguel Alejandro Bolivar Portilla, and all the contributors to the Spanish translation for being the first to completely translate the core pages of the docs!

Why Localization Matters

React already has many meetups and conferences around the world, but many programmers don’t use English as their primary language. We’d love to support local communities who use React by making our documentation available in most popular languages.

In the past, React community members have created unofficial translations for Chinese, Arabic, and Korean; by making an official channel for these translated docs we’re hoping to make them easier to find and help make sure that non-English-speaking users of React aren’t left behind.

Contributing

If you would like to help out on a current translation, check out the Languages page and click on the “Contribute” link for your language.

Can’t find your language? If you’d like to maintain your langauge’s translation fork, follow the instructions in the translation repo!

Backstory

Hi everyone! I’m Nat! You may know me as the polyhedra lady. For the past few weeks, I’ve been helping the React team coordinate their translation effort. Here’s how I did it.

Our original approach for translations was to use a SaaS platform that allows users to submit translations. There was already a pull request to integrate it and my original responsibility was to finish that integration. However, we had concerns about the feasibility of that integration and the current quality of translations on the platform. Our primary concern was ensuring that translations kept up to date with the main repo and didn’t become “stale”.

Dan encouraged me to look for alternate solutions, and we stumbled across how Vue maintained its translations — through different forks of the main repo on GitHub. In particular, the Japanese translation used a bot to periodically check for changes in the English repo and submits pull requests whenever there is a change.

This approach appealed to us for several reasons:

  • It was less code integration to get off the ground.
  • It encouraged active maintainers for each repo to ensure quality.
  • Contributors already understand GitHub as a platform and are motivated to contribute directly to the React organization.

We started of with an initial trial period of three languages: Spanish, Japanese, and Simplified Chinese. This allowed us to work out any kinks in our process and make sure future translations are set up for success. I wanted to give the translation teams freedom to choose whatever tools they felt comfortable with. The only requirement is a checklist that outlines the order of importance for translating pages.

After the trial period, we were ready to accept more languages. I created a script to automate the creation of the new language repo, and a site, Is React Translated Yet?, to track progress on the different translations. We started 10 new translations on our first day alone!

Because of the automation, the rest of the maintance went mostly smoothly. We eventually created a Slack channel to make it easier for translators to share information, and I released a guide solidifying the responsibilities of maintainers. Allowing translators to talk with each other was a great boon — for example, the Arabic, Persian, and Hebrew translations were able to talk to each other in order to get right-to-left text working!

The Bot

The most challenging part was getting the bot to sync changes from the English version of the site. Initially we were using the che-tsumi bot created by the Japanese Vue translation team, but we soon decided to build our own bot to suit our needs. In particular, the che-tsumi bot works by cherry picking new commits. This ended up causing a cavalade of new issues that were interconnected, especially when Hooks were released.

In the end, we decided that instead of cherry picking each commit, it made more sense to merge all new commits and create a pull request around once a day. Conflicts are merged as-is and listed in the pull request, leaving a checklist for maintainers to fix.

Creating the sync script was easy enough: it downloads the translated repo, adds the original as a remote, pulls from it, merges the conflicts, and creates a pull request.

The problem was finding a place for the bot to run. I’m a frontend developer for a reason — Heroku and its ilk are alien to me and endlessly frustrating. In fact, until this past Tuesday, I was running the script by hand on my local machine!

The biggest challenge was space. Each fork of the repo is around 100MB — which takes minutes to clone on my local machine. We have 32 forks, and the free tiers or most deployment platforms I checked limited you to 512MB of storage.

After lots of notepad calculations, I found a solution: delete each repo once we’ve finished the script and limit the concurrency of sync scripts that run at once to be within the storage requirements. Luckily, Heroku dynos have a much faster Internet connection and are able to clone even the React repo quickly.

There were other smaller issues that I ran into. I tried using the Heroku Scheduler add-on so I didn’t have to write any actual watch code, but it end up running too inconsistently, and I had an existential meltdown on Twitter when I couldn’t figure out how to send commits from the Heroku dyno. But in the end, this frontend engineer was able to get the bot working!

There are, as always, improvements I want to make to the bot. Right now it doesn’t check whether there is an outstanding pull request before pushing another one. It’s still hard to tell the exact change that happened in the original source, and it’s possible to miss out on a needed translation change. But I trust the maintainers we’ve chosen to work through these issues, and the bot is open source if anyone wants to help me make these improvements!

Thanks

Finally, I would like to extend my gratitude to the following people and groups:

  • All the translation maintainers and contributors who are helping translate React to more than thirty languages.
  • The Vue.js Japan User Group for initiating the idea of having bot-managed translations, and especially Hanatani Takuma for helping us understand their approach and helping maintain the Japanese translation.
  • Soichiro Miki for many contributions and thoughtful comments on the overall translation process, as well as for maintaining the Japanese translation.
  • Eric Nakagawa for managing our previous translation process.
  • Brian Vaughn for setting up the languages page and managing all the subdomains.

And finally, thank you to Dan Abramov for giving me this opportunity and being a great mentor along the way.

React v16.9.0 and the Roadmap Update

$
0
0

Today we are releasing React 16.9. It contains several new features, bugfixes, and new deprecation warnings to help prepare for a future major release.

New Deprecations

Renaming Unsafe Lifecycle Methods

Over a year ago, we announced that unsafe lifecycle methods are getting renamed:

  • componentWillMountUNSAFE_componentWillMount
  • componentWillReceivePropsUNSAFE_componentWillReceiveProps
  • componentWillUpdateUNSAFE_componentWillUpdate

React 16.9 does not contain breaking changes, and the old names continue to work in this release. But you will now see a warning when using any of the old names:

Warning: componentWillMount has been renamed, and is not recommended for use.

As the warning suggests, there are usually better approaches for each of the unsafe methods. However, maybe you don’t have the time to migrate or test these components. In that case, we recommend running a “codemod” script that renames them automatically:

cd your_project
npx react-codemod rename-unsafe-lifecycles

(Note that it says npx, not npm. npx is a utility that comes with Node 6+ by default.)

Running this codemod will replace the old names like componentWillMount with the new names like UNSAFE_componentWillMount:

Codemod in action

The new names like UNSAFE_componentWillMount will keep working in both React 16.9 and in React 17.x. However, the new UNSAFE_ prefix will help components with problematic patterns stand out during the code review and debugging sessions. (If you’d like, you can further discourage their use inside your app with the opt-in Strict Mode.)

Note

Learn more about our versioning policy and commitment to stability.

Deprecating javascript: URLs

URLs starting with javascript: are a dangerous attack surface because it’s easy to accidentally include unsanitized output in a tag like <a href> and create a security hole:

const userProfile = {
  website: "javascript: alert('you got hacked')",
};
// This will now warn:
<a href={userProfile.website}>Profile</a>

In React 16.9, this pattern continues to work, but it will log a warning. If you use javascript: URLs for logic, try to use React event handlers instead. (As a last resort, you can circumvent the protection with dangerouslySetInnerHTML, but it is highly discouraged and often leads to security holes.)

In a future major release, React will throw an error if it encounters a javascript: URL.

Deprecating “Factory” Components

Before compiling JavaScript classes with Babel became popular, React had support for a “factory” component that returns an object with a render method:

function FactoryComponent() {
  return { render() { return <div />; } }
}

This pattern is confusing because it looks too much like a function component — but it isn’t one. (A function component would just return the <div /> in the above example.)

This pattern was almost never used in the wild, and supporting it causes React to be slightly larger and slower than necessary. So we are deprecating this pattern in 16.9 and logging a warning if it’s encountered. If you rely on it, adding FactoryComponent.prototype = React.Component.prototype can serve as a workaround. Alternatively, you can convert it to either a class or a function component.

We don’t expect most codebases to be affected by this.

New Features

Async act() for Testing

React 16.8 introduced a new testing utility called act() to help you write tests that better match the browser behavior. For example, multiple state updates inside a single act() get batched. This matches how React already works when handling real browser events, and helps prepare your components for the future in which React will batch updates more often.

However, in 16.8 act() only supported synchronous functions. Sometimes, you might have seen a warning like this in a test but could not easily fix it:

An update to SomeComponent inside a test was not wrapped in act(...).

In React 16.9, act() also accepts asynchronous functions, and you can await its call:

await act(async () => {
  // ...
});

This solves the remaining cases where you couldn’t use act() before, such as when the state update was inside an asynchronous function. As a result, you should be able to fix all the remaining act() warnings in your tests now.

We’ve heard there wasn’t enough information about how to write tests with act(). The new Testing Recipes guide describes common scenarios, and how act() can help you write good tests. These examples use vanilla DOM APIs, but you can also use React Testing Library to reduce the boilerplate code. Many of its methods already use act() internally.

Please let us know on the issue tracker if you bump into any other scenarios where act() doesn’t work well for you, and we’ll try to help.

Performance Measurements with <React.Profiler>

In React 16.5, we introduced a new React Profiler for DevTools that helps find performance bottlenecks in your application. In React 16.9, we are also adding a programmatic way to gather measurements called <React.Profiler>. We expect that most smaller apps won’t use it, but it can be handy to track performance regressions over time in larger apps.

The <Profiler> measures how often a React application renders and what the “cost” of rendering is. Its purpose is to help identify parts of an application that are slow and may benefit from optimizations such as memoization.

A <Profiler> can be added anywhere in a React tree to measure the cost of rendering that part of the tree. It requires two props: an id (string) and an onRender callback (function) which React calls any time a component within the tree “commits” an update.

render(
  <Profiler id="application" onRender={onRenderCallback}>
    <App>
      <Navigation {...props} />
      <Main {...props} />
    </App>
  </Profiler>
);

To learn more about the Profiler and the parameters passed to the onRender callback, check out the Profiler docs.

Note:

Profiling adds some additional overhead, so it is disabled in the production build.

To opt into production profiling, React provides a special production build with profiling enabled. Read more about how to use this build at fb.me/react-profiling.

Notable Bugfixes

This release contains a few other notable improvements:

  • A crash when calling findDOMNode() inside a <Suspense> tree has been fixed.

  • A memory leak caused by retaining deleted subtrees has been fixed too.

  • An infinite loop caused by setState in useEffect now logs an error. (This is similar to the error you see when you call setState in componentDidUpdate in a class.)

We’re thankful to all the contributors who helped surface and fix these and other issues. You can find the full changelog below.

An Update to the Roadmap

In November 2018, we have posted this roadmap for the 16.x releases:

  • A minor 16.x release with React Hooks (past estimate: Q1 2019)
  • A minor 16.x release with Concurrent Mode (past estimate: Q2 2019)
  • A minor 16.x release with Suspense for Data Fetching (past estimate: mid 2019)

These estimates were too optimistic, and we’ve needed to adjust them.

tldr: We shipped Hooks on time, but we’re regrouping Concurrent Mode and Suspense for Data Fetching into a single release that we intend to release later this year.

In February, we shipped a stable 16.8 release including React Hooks, with React Native support coming a month later. However, we underestimated the follow-up work for this release, including the lint rules, developer tools, examples, and more documentation. This shifted the timeline by a few months.

Now that React Hooks are rolled out, the work on Concurrent Mode and Suspense for Data Fetching is in full swing. The new Facebook website that’s currently in active development is built on top of these features. Testing them with real code helped discover and address many issues before they can affect the open source users. Some of these fixes involved an internal redesign of these features, which has also caused the timeline to slip.

With this new understanding, here’s what we plan to do next.

One Release Instead of Two

Concurrent Mode and Suspense power the new Facebook website that’s in active development, so we are confident that they’re close to a stable state technically. We also now better understand the concrete steps before they are ready for open source adoption.

Originally we thought we would split Concurrent Mode and Suspense for Data Fetching into two releases. We’ve found that this sequencing is confusing to explain because these features are more related than we thought at first. So we plan to release support for both Concurrent Mode and Suspense for Data Fetching in a single combined release instead.

We don’t want to overpromise the release date again. Given that we rely on both of them in production code, we expect to provide a 16.x release with opt-in support for them this year.

An Update on Data Fetching

While React is not opinionated about how you fetch data, the first release of Suspense for Data Fetching will likely focus on integrating with opinionated data fetching libraries. For example, at Facebook we are using upcoming Relay APIs that integrate with Suspense. We will document how other opinionated libraries like Apollo can support a similar integration.

In the first release, we don’t intend to focus on the ad-hoc “fire an HTTP request” solution we used in earlier demos (also known as “React Cache”). However, we expect that both we and the React community will be exploring that space in the months after the initial release.

An Update on Server Rendering

We have started the work on the new Suspense-capable server renderer, but we don’t expect it to be ready for the initial release of Concurrent Mode. This release will, however, provide a temporary solution that lets the existing server renderer emit HTML for Suspense fallbacks immediately, and then render their real content on the client. This is the solution we are currently using at Facebook ourselves until the streaming renderer is ready.

Why Is It Taking So Long?

We’ve shipped the individual pieces leading up to Concurrent Mode as they became stable, including new context API, lazy loading with Suspense, and Hooks. We are also eager to release the other missing parts, but trying them at scale is an important part of the process. The honest answer is that it just took more work than we expected when we started. As always, we appreciate your questions and feedback on Twitter and in our issue tracker.

Installation

React

React v16.9.0 is available on the npm registry.

To install React 16 with Yarn, run:

yarn add react@^16.9.0 react-dom@^16.9.0

To install React 16 with npm, run:

npm install --save react@^16.9.0 react-dom@^16.9.0

We also provide UMD builds of React via a CDN:

<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

Refer to the documentation for detailed installation instructions.

Changelog

React

  • Add <React.Profiler> API for gathering performance measurements programmatically. (@bvaughn in #15172)
  • Remove unstable_ConcurrentMode in favor of unstable_createRoot. (@acdlite in #15532)

React DOM

React DOM Server

  • Fix incorrect output for camelCase custom CSS property names. (@bedakb in #16167)

React Test Utilities and Test Renderer

Introducing the New React DevTools

$
0
0

We are excited to announce a new release of the React Developer Tools, available today in Chrome, Firefox, and (Chromium) Edge!

What’s changed?

A lot has changed in version 4! At a high level, this new version should offer significant performance gains and an improved navigation experience. It also offers full support for React Hooks, including inspecting nested objects.

DevTools version 4 screenshot

Visit the interactive tutorial to try out the new version or see the changelog for demo videos and more details.

Which versions of React are supported?

react-dom

  • 0-14.x: Not supported
  • 15.x: Supported (except for the new component filters feature)
  • 16.x: Supported

react-native

  • 0-0.61: Not supported
  • 0.62: Will be supported (when 0.62 is released)

How do I get the new DevTools?

React DevTools is available as an extension for Chrome and Firefox. If you have already installed the extension, it should update automatically within the next couple of hours.

If you use the standalone shell (e.g. in React Native or Safari), you can install the new version from NPM:

npm install -g react-devtools@^4

Where did all of the DOM elements go?

The new DevTools provides a way to filter components from the tree to make it easier to navigate deeply nested hierarchies. Host nodes (e.g. HTML <div>, React Native <View>) are hidden by default, but this filter can be disabled:

DevTools component filters

How do I get the old version back?

If you are working with React Native version 60 (or older) you can install the previous release of DevTools from NPM:

npm install --dev react-devtools@^3

For older versions of React DOM (v0.14 or earlier) you will need to build the extension from source:

# Checkout the extension source
git clone https://github.com/facebook/react-devtools

cd react-devtools

# Install dependencies and build the unpacked extension
yarn install
yarn build:extension

# Follow the on-screen instructions to complete installation

Thank you!

We’d like to thank everyone who tested the early release of DevTools version 4. Your feedback helped improve this initial release significantly.

We still have many exciting features planned and feedback is always welcome! Please feel free to open a GitHub issue or tag @reactjs on Twitter.


Preparing for the Future with React Prereleases

$
0
0

To share upcoming changes with our partners in the React ecosystem, we’re establishing official prerelease channels. We hope this process will help us make changes to React with confidence, and give developers the opportunity to try out experimental features.

This post will be most relevant to developers who work on frameworks, libraries, or developer tooling. Developers who use React primarily to build user-facing applications should not need to worry about our prerelease channels.

React relies on a thriving open source community to file bug reports, open pull requests, and submit RFCs. To encourage feedback, we sometimes share special builds of React that include unreleased features.

Because the source of truth for React is our public GitHub repository, it’s always been possible to build a copy of React that includes the latest changes. However it’s much easier for developers to install React from npm, so we occasionally publish prerelease builds to the npm registry. A recent example is the 16.7 alpha, which included an early version of the Hooks API.

We would like to make it even easier for developers to test prerelease builds of React, so we’re formalizing our process with three separate release channels.

Release Channels

The information in this post is also available on our Release Channels page. We will update that document whenever there are changes to our release process.

Each of React’s release channels is designed for a distinct use case:

  • Latest is for stable, semver React releases. It’s what you get when you install React from npm. This is the channel you’re already using today. Use this for all user-facing React applications.
  • Next tracks the master branch of the React source code repository. Think of these as release candidates for the next minor semver release. Use this for integration testing between React and third party projects.
  • Experimental includes experimental APIs and features that aren’t available in the stable releases. These also track the master branch, but with additional feature flags turned on. Use this to try out upcoming features before they are released.

All releases are published to npm, but only Latest uses semantic versioning. Prereleases (those in the Next and Experimental channels) have versions generated from a hash of their contents, e.g. 0.0.0-1022ee0ec for Next and 0.0.0-experimental-1022ee0ec for Experimental.

The only officially supported release channel for user-facing applications is Latest. Next and Experimental releases are provided for testing purposes only, and we provide no guarantees that behavior won’t change between releases. They do not follow the semver protocol that we use for releases from Latest.

By publishing prereleases to the same registry that we use for stable releases, we are able to take advantage of the many tools that support the npm workflow, like unpkg and CodeSandbox.

Latest Channel

Latest is the channel used for stable React releases. It corresponds to the latest tag on npm. It is the recommended channel for all React apps that are shipped to real users.

If you’re not sure which channel you should use, it’s Latest. If you’re a React developer, this is what you’re already using.

You can expect updates to Latest to be extremely stable. Versions follow the semantic versioning scheme. Learn more about our commitment to stability and incremental migration in our versioning policy.

Next Channel

The Next channel is a prerelease channel that tracks the master branch of the React repository. We use prereleases in the Next channel as release candidates for the Latest channel. You can think of Next as a superset of Latest that is updated more frequently.

The degree of change between the most recent Next release and the most recent Latest release is approximately the same as you would find between two minor semver releases. However, the Next channel does not conform to semantic versioning. You should expect occasional breaking changes between successive releases in the Next channel.

Do not use prereleases in user-facing applications.

Releases in Next are published with the next tag on npm. Versions are generated from a hash of the build’s contents, e.g. 0.0.0-1022ee0ec.

Using the Next Channel for Integration Testing

The Next channel is designed to support integration testing between React and other projects.

All changes to React go through extensive internal testing before they are released to the public. However, there are a myriad of environments and configurations used throughout the React ecosystem, and it’s not possible for us to test against every single one.

If you’re the author of a third party React framework, library, developer tool, or similar infrastructure-type project, you can help us keep React stable for your users and the entire React community by periodically running your test suite against the most recent changes. If you’re interested, follow these steps:

  • Set up a cron job using your preferred continuous integration platform. Cron jobs are supported by both CircleCI and Travis CI.

  • In the cron job, update your React packages to the most recent React release in the Next channel, using next tag on npm. Using the npm cli:

    npm update react@next react-dom@next

    Or yarn:

    yarn upgrade react@next react-dom@next
  • Run your test suite against the updated packages.

  • If everything passes, great! You can expect that your project will work with the next minor React release.

  • If something breaks unexpectedly, please let us know by filing an issue.

A project that uses this workflow is Next.js. (No pun intended! Seriously!) You can refer to their CircleCI configuration as an example.

Experimental Channel

Like Next, the Experimental channel is a prerelease channel that tracks the master branch of the React repository. Unlike Next, Experimental releases include additional features and APIs that are not ready for wider release.

Usually, an update to Next is accompanied by a corresponding update to Experimental. They are based on the same source revision, but are built using a different set of feature flags.

Experimental releases may be significantly different than releases to Next and Latest. Do not use Experimental releases in user-facing applications. You should expect frequent breaking changes between releases in the Experimental channel.

Releases in Experimental are published with the experimental tag on npm. Versions are generated from a hash of the build’s contents, e.g. 0.0.0-experimental-1022ee0ec.

What Goes Into an Experimental Release?

Experimental features are ones that are not ready to be released to the wider public, and may change drastically before they are finalized. Some experiments may never be finalized — the reason we have experiments is to test the viability of proposed changes.

For example, if the Experimental channel had existed when we announced Hooks, we would have released Hooks to the Experimental channel weeks before they were available in Latest.

You may find it valuable to run integration tests against Experimental. This is up to you. However, be advised that Experimental is even less stable than Next. We do not guarantee any stability between Experimental releases.

How Can I Learn More About Experimental Features?

Experimental features may or may not be documented. Usually, experiments aren’t documented until they are close to shipping in Next or Stable.

If a feature is not documented, they may be accompanied by an RFC.

We will post to the React blog when we’re ready to announce new experiments, but that doesn’t mean we will publicize every experiment.

You can always refer to our public GitHub repository’s history for a comprehensive list of changes.

Building Great User Experiences with Concurrent Mode and Suspense

$
0
0

At React Conf 2019 we announced an experimental release of React that supports Concurrent Mode and Suspense. In this post we’ll introduce best practices for using them that we’ve identified through the process of building the new facebook.com.

This post will be most relevant to people working on data fetching libraries for React.

It shows how to best integrate them with Concurrent Mode and Suspense. The patterns introduced here are based on Relay — our library for building data-driven UIs with GraphQL. However, the ideas in this post apply to other GraphQL clients as well as libraries using REST or other approaches.

This post is aimed at library authors. If you’re primarily an application developer, you might still find some interesting ideas here, but don’t feel like you have to read it in its entirety.

Talk Videos

If you prefer to watch videos, some of the ideas from this blog post have been referenced in several React Conf 2019 presentations:

This post presents a deeper dive on implementing a data fetching library with Suspense.

Putting User Experience First

The React team and community has long placed a deserved emphasis on developer experience: ensuring that React has good error messages, focusing on components as a way to reason locally about app behavior, crafting APIs that are predictable and encourage correct usage by design, etc. But we haven’t provided enough guidance on the best ways to achieve a great user experience in large apps.

For example, the React team has focused on framework performance and providing tools for developers to debug and tune application performance (e.g. React.memo). But we haven’t been as opinionated about the high-level patterns that make the difference between fast, fluid apps and slow, janky ones. We always want to ensure that React remains approachable to new users and supports a variety of use-cases — not every app has to be “blazing” fast. But as a community we can and should aim high. We should make it as easy as possible to build apps that start fast and stay fast, even as they grow in complexity, for users on varying devices and networks around the world.

Concurrent Mode and Suspense are experimental features that can help developers achieve this goal. We first introduced them at JSConf Iceland in 2018, intentionally sharing details very early to give the community time to digest the new concepts and to set the stage for subsequent changes. Since then we’ve completed related work, such as the new Context API and the introduction of Hooks, which are designed in part to help developers naturally write code that is more compatible with Concurrent Mode. But we didn’t want to implement these features and release them without validating that they work. So over the past year, the React, Relay, web infrastructure, and product teams at Facebook have all collaborated closely to build a new version of facebook.com that deeply integrates Concurrent Mode and Suspense to create an experience with a more fluid and app-like feel.

Thanks to this project, we’re more confident than ever that Concurrent Mode and Suspense can make it easier to deliver great, fast user experiences. But doing so requires rethinking how we approach loading code and data for our apps. Effectively all of the data-fetching on the new facebook.com is powered by Relay Hooks — new Hooks-based Relay APIs that integrate with Concurrent Mode and Suspense out of the box.

Relay Hooks — and GraphQL — won’t be for everyone, and that’s ok! Through our work on these APIs we’ve identified a set of more general patterns for using Suspense. Even if Relay isn’t the right fit for you, we think the key patterns we’ve introduced with Relay Hooks can be adapted to other frameworks.

Best Practices for Suspense

It’s tempting to focus only on the total startup time for an app — but it turns out that users’ perception of performance is determined by more than the absolute loading time. For example, when comparing two apps with the same absolute startup time, our research shows that users will generally perceive the one with fewer intermediate loading states and fewer layout changes as having loaded faster. Suspense is a powerful tool for carefully orchestrating an elegant loading sequence with a few, well-defined states that progressively reveal content. But improving perceived performance only goes so far — our apps still shouldn’t take forever to fetch all of their code, data, images, and other assets.

The traditional approach to loading data in React apps involves what we refer to as “fetch-on-render”. First we render a component with a spinner, then fetch data on mount (componentDidMount or useEffect), and finally update to render the resulting data. It’s certainly possible to use this pattern with Suspense: instead of initially rendering a placeholder itself, a component can “suspend” — indicate to React that it isn’t ready yet. This will tell React to find the nearest ancestor <Suspense fallback={<Placeholder/>}>, and render its fallback instead. If you watched earlier Suspense demos this example may feel familiar — it’s how we originally imagined using Suspense for data-fetching.

It turns out that this approach has some limitations. Consider a page that shows a social media post by a user, along with comments on that post. That might be structured as a <Post> component that renders both the post body and a <CommentList> to show the comments. Using the fetch-on-render approach described above to implement this could cause sequential round trips (sometimes referred to as a “waterfall”). First the data for the <Post> component would be fetched and then the data for <CommentList> would be fetched, increasing the time it takes to show the full page.

There’s also another often-overlooked downside to this approach. If <Post> eagerly requires (or imports) the <CommentList> component, our app will have to wait to show the post body while the code for the comments is downloading. We could lazily load <CommentList>, but then that would delay fetching comments data and increase the time to show the full page. How do we resolve this problem without compromising on the user experience?

Render As You Fetch

The fetch-on-render approach is widely used by React apps today and can certainly be used to create great apps. But can we do even better? Let’s step back and consider our goal.

In the above <Post> example, we’d ideally show the more important content — the post body — as early as possible, without negatively impacting the time to show the full page (including comments). Let’s consider the key constraints on any solution and look at how we can achieve them:

  • Showing the more important content (the post body) as early as possible means that we need to load the code and data for the view incrementally. We don’t want to block showing the post body on the code for <CommentList> being downloaded, for example.
  • At the same time we don’t want to increase the time to show the full page including comments. So we need to start loading the code and data for the comments as soon as possible, ideally in parallel with loading the post body.

This might sound difficult to achieve — but these constraints are actually incredibly helpful. They rule out a large number of approaches and spell out a solution for us. This brings us to the key patterns we’ve implemented in Relay Hooks, and that can be adapted to other data-fetching libraries. We’ll look at each one in turn and then see how they add up to achieve our goal of fast, delightful loading experiences:

  1. Parallel data and view trees
  2. Fetch in event handlers
  3. Load data incrementally
  4. Treat code like data

Parallel Data and View Trees

One of the most appealing things about the fetch-on-render pattern is that it colocates what data a component needs with how to render that data. This colocation is great — an example of how it makes sense to group code by concerns and not by technologies. All the issues we saw above were due to when we fetch data in this approach: upon rendering. We need to be able to fetch data before we’ve rendered the component. The only way to achieve that is by extracting the data dependencies into parallel data and view trees.

Here’s how that works in Relay Hooks. Continuing our example of a social media post with body and comments, here’s how we might define it with Relay Hooks:

// Post.js
function Post(props) {
  // Given a reference to some post - `props.post` - *what* data
  // do we need about that post?
  const postData = useFragment(graphql`
    fragment PostData on Post @refetchable(queryName: "PostQuery") {
      author
      title
      # ...  more fields ...
    }
  `, props.post);

  // Now that we have the data, how do we render it?
  return (
    <div>
      <h1>{postData.title}</h1>
      <h2>by {postData.author}</h2>
      {/* more fields  */}
    </div>
  );
}

Although the GraphQL is written within the component, Relay has a build step (Relay Compiler) that extracts these data-dependencies into separate files and aggregates the GraphQL for each view into a single query. So we get the benefit of colocating concerns, while at runtime having parallel data and view trees. Other frameworks could achieve a similar effect by allowing developers to define data-fetching logic in a sibling file (maybe Post.data.js), or perhaps integrate with a bundler to allow defining data dependencies with UI code and automatically extracting it, similar to Relay Compiler.

The key is that regardless of the technology we’re using to load our data — GraphQL, REST, etc — we can separate what data to load from how and when to actually load it. But once we do that, how and when do we fetch our data?

Fetch in Event Handlers

Imagine that we’re about to navigate from a list of a user’s posts to the page for a specific post. We’ll need to download the code for that page — Post.js — and also fetch its data.

Waiting until we render the component has problems as we saw above. The key is to start fetching code and data for a new view in the same event handler that triggers showing that view. We can either fetch the data within our router — if our router supports preloading data for routes — or in the click event on the link that triggered the navigation. It turns out that the React Router folks are already hard at work on building APIs to support preloading data for routes. But other routing frameworks can implement this idea too.

Conceptually, we want every route definition to include two things: what component to render and what data to preload, as a function of the route/url params. Here’s what such a route definition might look like. This example is loosely inspired by React Router’s route definitions and is primarily intended to demonstrate the concept, not a specific API:

// PostRoute.js (GraphQL version)

// Relay generated query for loading Post data
import PostQuery from './__generated__/PostQuery.graphql';

const PostRoute = {
  // a matching expression for which paths to handle
  path: '/post/:id',

  // what component to render for this route
  component: React.lazy(() => import('./Post')),

  // data to load for this route, as function of the route
  // parameters
  prepare: routeParams => {
    // Relay extracts queries from components, allowing us to reference
    // the data dependencies -- data tree -- from outside.
    const postData = preloadQuery(PostQuery, {
      postId: routeParams.id,
    });

    return { postData };
  },
};

export default PostRoute;

Given such a definition, a router can:

  • Match a URL to a route definition.
  • Call the prepare() function to start loading that route’s data. Note that prepare() is synchronous — we don’t wait for the data to be ready, since we want to start rendering more important parts of the view (like the post body) as quickly as possible.
  • Pass the preloaded data to the component. If the component is ready — the React.lazy dynamic import has completed — the component will render and try to access its data. If not, React.lazy will suspend until the code is ready.

This approach can be generalized to other data-fetching solutions. An app that uses REST might define a route like this:

// PostRoute.js (REST version)

// Manually written logic for loading the data for the component
import PostData from './Post.data';

const PostRoute = {
  // a matching expression for which paths to handle
  path: '/post/:id',

  // what component to render for this route
  component: React.lazy(() => import('./Post')),

  // data to load for this route, as function of the route
  // parameters
  prepare: routeParams => {
    const postData = preloadRestEndpoint(
      PostData.endpointUrl, 
      {
        postId: routeParams.id,
      },
    );
    return { postData };
  },
};

export default PostRoute;

This same approach can be employed not just for routing, but in other places where we show content lazily or based on user interaction. For example, a tab component could eagerly load the first tab’s code and data, and then use the same pattern as above to load the code and data for other tabs in the tab-change event handler. A component that displays a modal could preload the code and data for the modal in the click handler that triggers opening the modal, and so on.

Once we’ve implemented the ability to start loading code and data for a view independently, we have the option to go one step further. Consider a <Link to={path} /> component that links to a route. If the user hovers over that link, there’s a reasonable chance they’ll click it. And if they press the mouse down, there’s an even better chance that they’ll complete the click. If we can load code and data for a view after the user clicks, we can also start that work before they click, getting a head start on preparing the view.

Best of all, we can centralize that logic in a few key places — a router or core UI components — and get any performance benefits automatically throughout our app. Of course preloading isn’t always beneficial. It’s something an application would tune based on the user’s device or network speed to avoid eating up user’s data plans. But the pattern here makes it easier to centralize the implementation of preloading and the decision of whether to enable it or not.

Load Data Incrementally

The above patterns — parallel data/view trees and fetching in event handlers — let us start loading all the data for a view earlier. But we still want to be able to show more important parts of the view without waiting for all of our data. At Facebook we’ve implemented support for this in GraphQL and Relay in the form of some new GraphQL directives (annotations that affect how/when data is delivered, but not what data). These new directives, called @defer and @stream, allow us to retrieve data incrementally. For example, consider our <Post> component from above. We want to show the body without waiting for the comments to be ready. We can achieve this with @defer and <Suspense>:

// Post.js
function Post(props) {
  const postData = useFragment(graphql`
    fragment PostData on Post {
      author
      title

      # fetch data for the comments, but don't block on it being ready
      ...CommentList @defer
    }
  `, props.post);

  return (
    <div>
      <h1>{postData.title}</h1>
      <h2>by {postData.author}</h2>
      {/* @defer pairs naturally w <Suspense> to make the UI non-blocking too */}
      <Suspense fallback={<Spinner/>}>
        <CommentList post={post} />
      </Suspense>
    </div>
  );
}

Here, our GraphQL server will stream back the results, first returning the author and title fields and then returning the comment data when it’s ready. We wrap <CommentList> in a <Suspense> boundary so that we can render the post body before <CommentList> and its data are ready. This same pattern can be applied to other frameworks as well. For example, apps that call a REST API might make parallel requests to fetch the body and comments data for a post to avoid blocking on all the data being ready.

Treat Code Like Data

But there’s one thing that’s still missing. We’ve shown how to preload data for a route — but what about code? The example above cheated a bit and used React.lazy. However, React.lazy is, as the name implies, lazy. It won’t start downloading code until the lazy component is actually rendered — it’s “fetch-on-render” for code!

To solve this, the React team is considering APIs that would allow bundle splitting and eager preloading for code as well. That would allow a user to pass some form of lazy component to a router, and for the router to trigger loading the code alongside its data as early as possible.

Putting It All Together

To recap, achieving a great loading experience means that we need to start loading code and data as early as possible, but without waiting for all of it to be ready. Parallel data and view trees allow us to load the data for a view in parallel with loading the view (code) itself. Fetching in an event handler means we can start loading data as early as possible, and even optimistically preload a view when we have enough confidence that a user will navigate to it. Loading data incrementally allows us to load important data earlier without delaying the fetching of less important data. And treating code as data — and preloading it with similar APIs — allows us to load it earlier too.

Using These Patterns

These patterns aren’t just ideas — we’ve implemented them in Relay Hooks and are using them in production throughout the new facebook.com (which is currently in beta testing). If you’re interested in using or learning more about these patterns, here are some resources:

  • The React Concurrent docs explore how to use Concurrent Mode and Suspense and go into more detail about many of these patterns. It’s a great resource to learn more about the APIs and use-cases they support.
  • The experimental release of Relay Hooks implements the patterns described here.
  • We’ve implemented two similar example apps that demonstrate these concepts:

    • The Relay Hooks example app uses GitHub’s public GraphQL API to implement a simple issue tracker app. It includes nested route support with code and data preloading. The code is fully commented — we encourage cloning the repo, running the app locally, and exploring how it works.
    • We also have a non-GraphQL version of the app that demonstrates how these concepts can be applied to other data-fetching libraries.

While the APIs around Concurrent Mode and Suspense are still experimental, we’re confident that the ideas in this post are proven by practice. However, we understand that Relay and GraphQL aren’t the right fit for everyone. That’s ok! We’re actively exploring how to generalize these patterns to approaches such as REST, and are exploring ideas for a more generic (ie non-GraphQL) API for composing a tree of data dependencies. In the meantime, we’re excited to see what new libraries will emerge that implement the patterns described in this post to make it easier to build great, fast user experiences.

React v16.13.0

$
0
0

Today we are releasing React 16.13.0. It contains bugfixes and new deprecation warnings to help prepare for a future major release.

New Warnings

Warnings for some updates during render

A React component should not cause side effects in other components during rendering.

It is supported to call setState during render, but only for the same component. If you call setState during a render on a different component, you will now see a warning:

Warning: Cannot update a component from inside the function body of a different component.

This warning will help you find application bugs caused by unintentional state changes. In the rare case that you intentionally want to change the state of another component as a result of rendering, you can wrap the setState call into useEffect.

Warnings for conflicting style rules

When dynamically applying a style that contains longhand and shorthand versions of CSS properties, particular combinations of updates can cause inconsistent styling. For example:

<div style={toggle ? 
  { background: 'blue', backgroundColor: 'red' } : 
  { backgroundColor: 'red' }
}>
  ...
</div> 

You might expect this <div> to always have a red background, no matter the value of toggle. However, on alternating the value of toggle between true and false, the background color start as red, then alternates between transparent and blue, as you can see in this demo.

React now detects conflicting style rules and logs a warning. To fix the issue, don’t mix shorthand and longhand versions of the same CSS property in the style prop.

Warnings for some deprecated string refs

String Refs is an old legacy API which is discouraged and is going to be deprecated in the future:

<Button ref="myRef" />

(Don’t confuse String Refs with refs in general, which remain fully supported.)

In the future, we will provide an automated script (a “codemod”) to migrate away from String Refs. However, some rare cases can’t be migrated automatically. This release adds a new warning only for those cases in advance of the deprecation.

For example, it will fire if you use String Refs together with the Render Prop pattern:

class ClassWithRenderProp extends React.Component {
  componentDidMount() {
    doSomething(this.refs.myRef);
  }
  render() {
    return this.props.children();
  }
}

class ClassParent extends React.Component {
  render() {
    return (
      <ClassWithRenderProp>
        {() => <Button ref="myRef" />}
      </ClassWithRenderProp>
    );
  }
}

Code like this often indicates bugs. (You might expect the ref to be available on ClassParent, but instead it gets placed on ClassWithRenderProp).

You most likely don’t have code like this. If you do and it is intentional, convert it to React.createRef() instead:

class ClassWithRenderProp extends React.Component {
  myRef = React.createRef();
  componentDidMount() {
    doSomething(this.myRef.current);
  }
  render() {
    return this.props.children(this.myRef);
  }
}

class ClassParent extends React.Component {
  render() {
    return (
      <ClassWithRenderProp>
        {myRef => <Button ref={myRef} />}
      </ClassWithRenderProp>
    );
  }
}

Note

To see this warning, you need to have the babel-plugin-transform-react-jsx-self installed in your Babel plugins. It must only be enabled in development mode.

If you use Create React App or have the “react” preset with Babel 7+, you already have this plugin installed by default.

Deprecating React.createFactory

React.createFactory is a legacy helper for creating React elements. This release adds a deprecation warning to the method. It will be removed in a future major version.

Replace usages of React.createFactory with regular JSX. Alternately, you can copy and paste this one-line helper or publish it as a library:

let createFactory = type => React.createElement.bind(null, type);

It does exactly the same thing.

Deprecating ReactDOM.unstable_createPortal in favor of ReactDOM.createPortal

When React 16 was released, createPortal became an officially supported API.

However, we kept unstable_createPortal as a supported alias to keep the few libraries that adopted it working. We are now deprecating the unstable alias. Use createPortal directly instead of unstable_createPortal. It has exactly the same signature.

Other Improvements

Component stacks in hydration warnings

React adds component stacks to its development warnings, enabling developers to isolate bugs and debug their programs. This release adds component stacks to a number of development warnings that didn’t previously have them. As an example, consider this hydration warning from the previous versions:

A screenshot of the console warning, simply stating the nature of the hydration mismatch:

While it’s pointing out an error with the code, it’s not clear where the error exists, and what to do next. This release adds a component stack to this warning, which makes it look like this:

A screenshot of the console warning, stating the nature of the hydration mismatch, but also including a component stack :

This makes it clear where the problem is, and lets you locate and fix the bug faster.

Notable bugfixes

This release contains a few other notable improvements:

  • In Strict Development Mode, React calls lifecycle methods twice to flush out any possible unwanted side effects. This release adds that behaviour to shouldComponentUpdate. This shouldn’t affect most code, unless you have side effects in shouldComponentUpdate. To fix this, move the code with side effects into componentDidUpdate.

  • In Strict Development Mode, the warnings for usage of the legacy context API didn’t include the stack for the component that triggered the warning. This release adds the missing stack to the warning.

  • onMouseEnter now doesn’t trigger on disabled <button> elements.

  • ReactDOM was missing a version export since we published v16. This release adds it back. We don’t recommend using it in your application logic, but it’s useful when debugging issues with mismatching / multiple versions of ReactDOM on the same page.

We’re thankful to all the contributors who helped surface and fix these and other issues. You can find the full changelog below.

Installation

React

React v16.13.0 is available on the npm registry.

To install React 16 with Yarn, run:

yarn add react@^16.13.0 react-dom@^16.13.0

To install React 16 with npm, run:

npm install --save react@^16.13.0 react-dom@^16.13.0

We also provide UMD builds of React via a CDN:

<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

Refer to the documentation for detailed installation instructions.

Changelog

React

  • Warn when a string ref is used in a manner that’s not amenable to a future codemod (@lunaruan in #17864)
  • Deprecate React.createFactory() (@trueadm in #17878)

React DOM

Concurrent Mode (Experimental)

React v17.0 Release Candidate: No New Features

$
0
0

Today, we are publishing the first Release Candidate for React 17. It has been two and a half years since the previous major release of React, which is a long time even by our standards! In this blog post, we will describe the role of this major release, what changes you can expect in it, and how you can try this release.

No New Features

The React 17 release is unusual because it doesn’t add any new developer-facing features. Instead, this release is primarily focused on making it easier to upgrade React itself.

We’re actively working on the new React features, but they’re not a part of this release. The React 17 release is a key part of our strategy to roll them out without leaving anyone behind.

In particular, React 17 is a “stepping stone” release that makes it safer to embed a tree managed by one version of React inside a tree managed by a different version of React.

Gradual Upgrades

For the past seven years, React upgrades have been “all-or-nothing”. Either you stay on an old version, or you upgrade your whole app to a new version. There was no in-between.

This has worked out so far, but we are running into the limits of the “all-or-nothing” upgrade strategy. Some API changes, for example, deprecating the legacy context API, are impossible to do in an automated way. Even though most apps written today don’t ever use them, we still support them in React. We have to choose between supporting them in React indefinitely or leaving some apps behind on an old version of React. Both of these options aren’t great.

So we wanted to provide another option.

React 17 enables gradual React upgrades. When you upgrade from React 15 to 16 (or, soon, from React 16 to 17), you would usually upgrade your whole app at once. This works well for many apps. But it can get increasingly challenging if the codebase was written more than a few years ago and isn’t actively maintained. And while it’s possible to use two versions of React on the page, until React 17 this has been fragile and caused problems with events.

We’re fixing many of those problems with React 17. This means that when React 18 and the next future versions come out, you will now have more options. The first option will be to upgrade your whole app at once, like you might have done before. But you will also have an option to upgrade your app piece by piece. For example, you might decide to migrate most of your app to React 18, but keep some lazy-loaded dialog or a subroute on React 17.

This doesn’t mean you have to do gradual upgrades. For most apps, upgrading all at once is still the best solution. Loading two versions of React — even if one of them is loaded lazily on demand — is still not ideal. However, for larger apps that aren’t actively maintained, this option may make sense to consider, and React 17 enables those apps to not get left behind.

To enable gradual updates, we’ve needed to make some changes to the React event system. React 17 is a major release because these changes are potentially breaking. In practice, we’ve only had to change fewer than twenty components out of 100,000+ so we expect that most apps can upgrade to React 17 without too much trouble. Tell us if you run into problems.

Demo of Gradual Upgrades

We’ve prepared an example repository demonstrating how to lazy-load an older version of React if necessary. This demo uses Create React App, but it should be possible to follow a similar approach with any other tool. We welcome demos using other tooling as pull requests.

Note

We’ve postponed other changes until after React 17. The goal of this release is to enable gradual upgrades. If upgrading to React 17 were too difficult, it would defeat its purpose.

Changes to Event Delegation

Technically, it has always been possible to nest apps developed with different versions of React. However, it was rather fragile because of how the React event system worked.

In React components, you usually write event handlers inline:

<button onClick={handleClick}>

The vanilla DOM equivalent to this code is something like:

myButton.addEventListener('click', handleClick);

However, for most events, React doesn’t actually attach them to the DOM nodes on which you declare them. Instead, React attaches one handler per event type directly at the document node. This is called event delegation. In addition to its performance benefits on large application trees, it also makes it easier to add new features like replaying events.

React has been doing event delegation automatically since its first release. When a DOM event fires on the document, React figures out which component to call, and then the React event “bubbles” upwards through your components. But behind the scenes, the native event has already bubbled up to the document level, where React installs its event handlers.

However, this is a problem for gradual upgrades.

If you have multiple React versions on the page, they all register event handlers at the top. This breaks e.stopPropagation(): if a nested tree has stopped propagation of an event, the outer tree would still receive it. This made it difficult to nest different versions of React. This concern is not hypothetical — for example, the Atom editor ran into this four years ago.

This is why we’re changing how React attaches events to the DOM under the hood.

In React 17, React will no longer attach event handlers at the document level. Instead, it will attach them to the root DOM container into which your React tree is rendered:

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

In React 16 and earlier, React would do document.addEventListener() for most events. React 17 will call rootNode.addEventListener() under the hood instead.

A diagram showing how React 17 attaches events to the roots rather than to the document

Thanks to this change, it is now safer to embed a React tree managed by one version inside a tree managed by a different React version. Note that for this to work, both of the versions would need to be 17 or higher, which is why upgrading to React 17 is important. In a way, React 17 is a “stepping stone” release that makes next gradual upgrades feasible.

This change also makes it easier to embed React into apps built with other technologies. For example, if the outer “shell” of your app is written in jQuery, but the newer code inside of it is written with React, e.stopPropagation() inside the React code would now prevent it from reaching the jQuery code — as you would expect. This also works in the other direction. If you no longer like React and want to rewrite your app — for example, in jQuery — you can start converting the outer shell from React to jQuery without breaking the event propagation.

We’ve confirmed that numerous problems reported over the years on our issue tracker related to integrating React with non-React code have been fixed by the new behavior.

Note

You might be wondering whether this breaks Portals outside of the root container. The answer is that React also listens to events on portal containers, so this is not an issue.

Fixing Potential Issues

As with any breaking change, it is likely some code would need to be adjusted. At Facebook, we had to adjust about 10 modules in total (out of many thousands) to work with this change.

For example, if you add manual DOM listeners with document.addEventListener(...), you might expect them to catch all React events. In React 16 and earlier, even if you call e.stopPropagation() in a React event handler, your custom document listeners would still receive them because the native event is already at the document level. With React 17, the propagation would stop (as requested!), so your document handlers would not fire:

document.addEventListener('click', function() {
  // This custom handler will no longer receive clicks
  // from React components that called e.stopPropagation()
});

You can fix code like this by converting your listener to use the capture phase. To do this, you can pass { capture: true } as the third argument to document.addEventListener:

document.addEventListener('click', function() {
  // Now this event handler uses the capture phase,
  // so it receives *all* click events below!
}, { capture: true });

Note how this strategy is more resilient overall — for example, it will probably fix existing bugs in your code that happen when e.stopPropagation() is called outside of a React event handler. In other words, event propagation in React 17 works closer to the regular DOM.

Other Breaking Changes

We’ve kept the breaking changes in React 17 to the minimum. For example, it doesn’t remove any of the methods that have been deprecated in the previous releases. However, it does include a few other breaking changes that have been relatively safe in our experience. In total, we’ve had to adjust fewer than 20 out of 100,000+ our components because of them.

Aligning with Browsers

We’ve made a couple of smaller changes related to the event system:

  • The onScroll event no longer bubbles to prevent common confusion.
  • React onFocus and onBlur events have switched to using the native focusin and focusout events under the hood, which more closely match React’s existing behavior and sometimes provide extra information.
  • Capture phase events (e.g. onClickCapture) now use real browser capture phase listeners.

These changes align React closer with the browser behavior and improve interoperability.

Note

Although React 17 switched from focus to focusin under the hood for the onFocus event, note that this has not affected the bubbling behavior. In React, onFocus event has always bubbled, and it continues to do so in React 17 because generally it is a more useful default. See this sandbox for the different checks you can add for different particular use cases.

No Event Pooling

React 17 removes the “event pooling” optimization from React. It doesn’t improve performance in modern browsers and confuses even experienced React users:

function handleChange(e) {
  setData(data => ({
    ...data,
    // This crashes in React 16 and earlier:
    text: e.target.value
  }));
}

This is because React reused the event objects between different events for performance in old browsers, and set all event fields to null in between them. With React 16 and earlier, you have to call e.persist() to properly use the event, or read the property you need earlier.

In React 17, this code works as you would expect. The old event pooling optimization has been fully removed, so you can read the event fields whenever you need them.

This is a behavior change, which is why we’re marking it as breaking, but in practice we haven’t seen it break anything at Facebook. (Maybe it even fixed a few bugs!) Note that e.persist() is still available on the React event object, but now it doesn’t do anything.

Effect Cleanup Timing

We are making the timing of the useEffect cleanup function more consistent.

useEffect(() => {
  // This is the effect itself.
  return () => {    // This is its cleanup.  };});

Most effects don’t need to delay screen updates, so React runs them asynchronously soon after the update has been reflected on the screen. (For the rare cases where you need an effect to block paint, e.g. to measure and position a tooltip, prefer useLayoutEffect.)

However, the effect cleanup function, when it exists, used to run synchronously in React 16. We’ve found that, similar to componentWillUnmount being synchronous in classes, this is not ideal for larger apps because it slows down large screen transitions (e.g. switching tabs).

In React 17, the effect cleanup function also runs asynchronously — for example, if the component is unmounting, the cleanup will run after the screen has been updated.

This mirrors how the effects themselves run more closely. In the rare cases where you might want to rely on the synchronous execution, you can switch to useLayoutEffect instead.

Note

You might be wondering whether this means that you’ll now be unable to fix warnings about setState on unmounted components. Don’t worry — React specifically checks for this case, and does not fire setState warnings in the short gap between unmounting and the cleanup. So code cancelling requests or intervals can almost always stay the same.

Additionally, React 17 executes the cleanup functions in the same order as the effects, according to their position in the tree. Previously, this order was occasionally different.

Potential Issues

We’ve only seen a couple of components break with this change, although reusable libraries may need to test it more thoroughly. One example of problematic code may look like this:

useEffect(() => {
  someRef.current.someSetupMethod();
  return () => {
    someRef.current.someCleanupMethod();
  };
});

The problem is that someRef.current is mutable, so by the time the cleanup function runs, it may have been set to null. The solution is to capture any mutable values inside the effect:

useEffect(() => {
  const instance = someRef.current;
  instance.someSetupMethod();
  return () => {
    instance.someCleanupMethod();
  };
});

We don’t expect this to be a common problem because our eslint-plugin-react-hooks/exhaustive-deps lint rule (make sure you use it!) has always warned about this.

Consistent Errors for Returning Undefined

In React 16 and earlier, returning undefined has always been an error:

function Button() {
  return; // Error: Nothing was returned from render
}

This is in part because it’s easy to return undefined unintentionally:

function Button() {
  // We forgot to write return, so this component returns undefined.
  // React surfaces this as an error instead of ignoring it.
  <button />;
}

Previously, React only did this for class and function components, but did not check the return values of forwardRef and memo components. This was due to a coding mistake.

In React 17, the behavior for forwardRef and memo components is consistent with regular function and class components. Returning undefined from them is an error.

let Button = forwardRef(() => {
  // We forgot to write return, so this component returns undefined.
  // React 17 surfaces this as an error instead of ignoring it.
  <button />;
});

let Button = memo(() => {
  // We forgot to write return, so this component returns undefined.
  // React 17 surfaces this as an error instead of ignoring it.
  <button />;
});

For the cases where you want to render nothing intentionally, return null instead.

Native Component Stacks

When you throw an error in the browser, the browser gives you a stack trace with JavaScript function names and their locations. However, JavaScript stacks are often not enough to diagnose a problem because the React tree hierarchy can be just as important. You want to know not just that a Button threw an error, but where in the React tree that Button is.

To solve this, React 16 started printing “component stacks” when you have an error. Still, they used to be inferior to the native JavaScript stacks. In particular, they were not clickable in the console because React didn’t know where the function was declared in the source code. Additionally, they were mostly useless in production. Unlike regular minified JavaScript stacks which can be automatically restored to the original function names with a sourcemap, with React component stacks you had to choose between production stacks and bundle size.

In React 17, the component stacks are generated using a different mechanism that stitches them together from the regular native JavaScript stacks. This lets you get the fully symbolicated React component stack traces in a production environment.

The way React implements this is somewhat unorthodox. Currently, the browsers don’t provide a way to get a function’s stack frame (source file and location). So when React catches an error, it will now reconstruct its component stack by throwing (and catching) a temporary error from inside each of the components above, when it is possible. This adds a small performance penalty for crashes, but it only happens once per component type.

If you’re curious, you can read more details in this pull request, but for the most part this exact mechanism shouldn’t affect your code. From your perspective, the new feature is that component stacks are now clickable (because they rely on the native browser stack frames), and that you can decode them in production like you would with regular JavaScript errors.

The part that constitutes a breaking change is that for this to work, React re-executes parts of some of the React functions and React class constructors above in the stack after an error is captured. Since render functions and class constructors shouldn’t have side effects (which is also important for server rendering), this should not pose any practical problems.

Removing Private Exports

Finally, the last notable breaking change is that we’ve removed some React internals that were previously exposed to other projects. In particular, React Native for Web used to depend on some internals of the event system, but that dependency was fragile and used to break.

In React 17, these private exports have been removed. As far as we’re aware, React Native for Web was the only project using them, and they have already completed a migration to a different approach that doesn’t depend on those private exports.

This means that the older versions of React Native for Web won’t be compatible with React 17, but the newer versions will work with it. In practice, this doesn’t change much because React Native for Web had to release new versions to adapt to internal React changes anyway.

Additionally, we’ve removed the ReactTestUtils.SimulateNative helper methods. They have never been documented, didn’t do quite what their names implied, and didn’t work with the changes we’ve made to the event system. If you want a convenient way to fire native browser events in tests, check out the React Testing Library instead.

Installation

We encourage you to try React 17.0 Release Candidate soon and raise any issues for the problems you might encounter in the migration. Keep in mind that a release candidate is more likely to contain bugs than a stable release, so don’t deploy it to production yet.

To install React 17 RC with npm, run:

npm install react@17.0.0-rc.0 react-dom@17.0.0-rc.0

To install React 17 RC with Yarn, run:

yarn add react@17.0.0-rc.0 react-dom@17.0.0-rc.0

We also provide UMD builds of React via a CDN:

<script crossorigin src="https://unpkg.com/react@17.0.0-rc.0/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17.0.0-rc.0/umd/react-dom.production.min.js"></script>

Refer to the documentation for detailed installation instructions.

Changelog

React

React DOM

  • Delegate events to roots instead of document. (@trueadm in #18195 and others)
  • Clean up all effects before running any next effects. (@bvaughn in #17947)
  • Run useEffect cleanup functions asynchronously. (@bvaughn in #17925)
  • Use browser focusin and focusout for onFocus and onBlur. (@trueadm in #19186)
  • Make all Capture events use the browser capture phase. (@trueadm in #19221)
  • Don’t emulate bubbling of the onScroll event. (@gaearon in #19464)
  • Throw if forwardRef or memo component returns undefined. (@gaearon in #19550)
  • Remove event pooling. (@trueadm in #18969)
  • Stop exposing internals that won’t be needed by React Native Web. (@necolas in #18483)
  • Disable console in the second render pass of DEV mode double render. (@sebmarkbage in #18547)
  • Deprecate the undocumented and misleading ReactTestUtils.SimulateNative API. (@gaearon in #13407)
  • Rename private field names used in the internals. (@gaearon in #18377)
  • Don’t call User Timing API in development. (@gaearon in #18417)
  • Disable console during the repeated render in Strict Mode. (@sebmarkbage in #18547)
  • In Strict Mode, double-render components without Hooks too. (@eps1lon in #18430)
  • Allow calling ReactDOM.flushSync during lifecycle methods (but warn). (@sebmarkbage in #18759)
  • Add the code property to the keyboard event objects. (@bl00mber in #18287)
  • Add the disableRemotePlayback property for video elements. (@tombrowndev in #18619)
  • Add the enterKeyHint property for input elements. (@eps1lon in #18634)
  • Warn when no value is provided to <Context.Provider>. (@charlie1404 in #19054)
  • Warn when memo or forwardRef components return undefined. (@bvaughn in #19550)
  • Improve the error message for invalid updates. (@JoviDeCroock in #18316)
  • Exclude forwardRef and memo from stack frames. (@sebmarkbage in #18559)
  • Improve the error message when switching between controlled and uncontrolled inputs. (@vcarl in #17070)
  • Fix setState hanging in development inside a closed iframe. (@gaearon in #19220)
  • Fix rendering bailout for lazy components with defaultProps. (@jddxf in #18539)
  • Fix a false positive warning when dangerouslySetInnerHTML is undefined. (@eps1lon in #18676)
  • Fix Test Utils with non-standard require implementation. (@just-boris in #18632)
  • Fix onBeforeInput reporting an incorrect event.type. (@eps1lon in #19561)
  • Use delegation for onSubmit and onReset events. (@gaearon in #19333)
  • Improve memory usage. (@trueadm in #18970)

React DOM Server

  • Make useCallback behavior consistent with useMemo for the server renderer. (@alexmckenley in #18783)
  • Fix state leaking when a function component throws. (@pmaccart in #19212)

React Test Renderer

Concurrent Mode (Experimental)

Introducing the New JSX Transform

$
0
0

Although React 17 doesn’t contain new features, it will provide support for a new version of the JSX transform. In this post, we will describe what it is and how to try it.

What’s a JSX Transform?

Browsers don’t understand JSX out of the box, so most React users rely on a compiler like Babel or TypeScript to transform JSX code into regular JavaScript. Many preconfigured toolkits like Create React App or Next.js also include a JSX transform under the hood.

Together with the React 17 release, we’ve wanted to make a few improvements to the JSX transform, but we didn’t want to break existing setups. This is why we worked with Babel to offer a new, rewritten version of the JSX transform for people who would like to upgrade.

Upgrading to the new transform is completely optional, but it has a few benefits:

  • With the new transform, you can use JSX without importing React.
  • Depending on your setup, its compiled output may slightly improve the bundle size.
  • It will enable future improvements that reduce the number of concepts you need to learn React.

This upgrade will not change the JSX syntax and is not required. The old JSX transform will keep working as usual, and there are no plans to remove the support for it.

React 17 RC already includes support for the new transform, so go give it a try! To make it easier to adopt, after React 17 is released, we also plan to backport its support to React 16.x, React 15.x, and React 0.14.x. You can find the upgrade instructions for different tools below.

Now let’s take a closer look at the differences between the old and the new transform.

What’s Different in the New Transform?

When you use JSX, the compiler transforms it into React function calls that the browser can understand. The old JSX transform turned JSX into React.createElement(...) calls.

For example, let’s say your source code looks like this:

import React from 'react';

function App() {
  return <h1>Hello World</h1>;
}

Under the hood, the old JSX transform turns it into regular JavaScript:

import React from 'react';

function App() {
  return React.createElement('h1', null, 'Hello world');
}

Note

Your source code doesn’t need to change in any way. We’re describing how the JSX transform turns your JSX source code into the JavaScript code a browser can understand.

However, this is not perfect:

To solve these issues, React 17 introduces two new entry points to the React package that are intended to only be used by compilers like Babel and TypeScript. Instead of transforming JSX to React.createElement, the new JSX transform automatically imports special functions from those new entry points in the React package and calls them.

Let’s say that your source code looks like this:

function App() {
  return <h1>Hello World</h1>;
}

This is what the new JSX transform compiles it to:

// Inserted by a compiler (don't import it yourself!)
import {jsx as _jsx} from 'react/jsx-runtime';

function App() {
  return _jsx('h1', { children: 'Hello world' });
}

Note how our original code did not need to import React to use JSX anymore! (But we would still need to import React in order to use Hooks or other exports that React provides.)

This change is fully compatible with all of the existing JSX code, so you won’t have to change your components. If you’re curious, you can check out the technical RFC for more details about how the new transform works.

Note

The functions inside react/jsx-runtime and react/jsx-dev-runtime must only be used by the compiler transform. If you need to manually create elements in your code, you should keep using React.createElement. It will continue to work and is not going away.

How to Upgrade to the New JSX Transform

If you aren’t ready to upgrade to the new JSX transform or if you are using JSX for another library, don’t worry. The old transform will not be removed and will continue to be supported.

If you want to upgrade, you will need two things:

  • A version of React that supports the new transform (currently, only React 17 RC supports it, but after React 17.0 has been released, we plan to make additional compatible releases for 0.14.x, 15.x, and 16.x).
  • A compatible compiler (see instructions for different tools below).

Since the new JSX transform doesn’t require React to be in scope, we’ve also prepared an automated script that will remove the unnecessary imports from your codebase.

Create React App

Create React App support has been added and will be available in the upcoming v4.0 release which is currently in beta testing.

Next.js

Next.js v9.5.3+ uses the new transform for compatible React versions.

Gatsby

Gatsby v2.24.5+ uses the new transform for compatible React versions.

Note

If you get this Gatsby error after upgrading to React 17.0.0-rc.2, run npm update to fix it.

Manual Babel Setup

Support for the new JSX transform is available in Babel v7.9.0 and above.

First, you’ll need to update to the latest Babel and plugin transform.

If you are using @babel/plugin-transform-react-jsx:

# for npm users
npm update @babel/core @babel/plugin-transform-react-jsx
# for yarn users
yarn upgrade @babel/core @babel/plugin-transform-react-jsx

If you are using @babel/preset-react:

# for npm users
npm update @babel/core @babel/preset-react
# for yarn users
yarn upgrade @babel/core @babel/preset-react

Currently, the old transform ("runtime": "classic") is the default option. To enable the new transform, you can pass {"runtime": "automatic"} as an option to @babel/plugin-transform-react-jsx or @babel/preset-react:

// If you are using @babel/preset-react
{
  "presets": [
    ["@babel/preset-react", {
      "runtime": "automatic"
    }]
  ]
}
// If you're using @babel/plugin-transform-react-jsx
{
  "plugins": [
    ["@babel/plugin-transform-react-jsx", {
      "runtime": "automatic"
    }]
  ]
}

Starting from Babel 8, "automatic" will be the default runtime for both plugins. For more information, check out the Babel documentation for @babel/plugin-transform-react-jsx and @babel/preset-react.

Note

If you use JSX with a library other than React, you can use the importSource option to import from that library instead — as long as it provides the necessary entry points. Alternatively, you can keep using the classic transform which will continue to be supported.

ESLint

If you are using eslint-plugin-react, the react/jsx-uses-react and react/react-in-jsx-scope rules are no longer necessary and can be turned off or removed.

{
  // ...
  "rules": {
    // ...
    "react/jsx-uses-react": "off",
    "react/react-in-jsx-scope": "off"
  }
}

TypeScript

TypeScript supports the JSX transform in v4.1 beta.

Flow

Flow supports the new JSX transform in v0.126.0 and up.

Removing Unused React Imports

Because the new JSX transform will automatically import the necessary react/jsx-runtime functions, React will no longer need to be in scope when you use JSX. This might lead to unused React imports in your code. It doesn’t hurt to keep them, but if you’d like to remove them, we recommend running a “codemod” script to remove them automatically:

cd your_project
npx react-codemod update-react-imports

Note

If you’re getting errors when running the codemod, try specifying a different JavaScript dialect when npx react-codemod update-react-imports asks you to choose one. In particular, at this moment the “JavaScript with Flow” setting supports newer syntax than the “JavaScript” setting even if you don’t use Flow. File an issue if you run into problems.

Keep in mind that the codemod output will not always match your project’s coding style, so you might want to run Prettier after the codemod finishes for consistent formatting.

Running this codemod will:

  • Remove all unused React imports as a result of upgrading to the new JSX transform.
  • Change all default React imports (i.e. import React from "react") to destructured named imports (ex. import { useState } from "react") which is the preferred style going into the future. This codemod will not affect the existing namespace imports (i.e. import * as React from "react") which is also a valid style. The default imports will keep working in React 17, but in the longer term we encourage moving away from them.

For example,

import React from 'react';

function App() {
  return <h1>Hello World</h1>;
}

will be replaced with

function App() {
  return <h1>Hello World</h1>;
}

If you use some other import from React — for example, a Hook — then the codemod will convert it to a named import.

For example,

import React from 'react';

function App() {
  const [text, setText] = React.useState('Hello World');
  return <h1>{text}</h1>;
}

will be replaced with

import { useState } from 'react';

function App() {
  const [text, setText] = useState('Hello World');
  return <h1>{text}</h1>;
}

In addition to cleaning up unused imports, this will also help you prepare for a future major version of React (not React 17) which will support ES Modules and not have a default export.

Thanks

We’d like to thank Babel, TypeScript, Create React App, Next.js, Gatsby, ESLint, and Flow maintainers for their help implementing and integrating the new JSX transform. We also want to thank the React community for their feedback and discussion on the related technical RFC.

Viewing all 193 articles
Browse latest View live