From 544c0bc5c9534cd819495bbd64e4dc1996828d30 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 6 Feb 2018 13:26:10 -0800 Subject: [PATCH 01/61] Migrated async update from Quip to Markdown --- .../2018-02-07-update-on-async-rendering.md | 171 ++++++++++++++++++ .../strict-mode-unsafe-lifecycles-warning.png | Bin 0 -> 53348 bytes .../adding-event-listeners-after.js | 39 ++++ .../adding-event-listeners-before.js | 25 +++ .../enabling-strict-mode.js | 21 +++ .../fetching-external-data-after.js | 33 ++++ .../fetching-external-data-before.js | 23 +++ .../initializing-state-after.js | 10 + .../initializing-state-before.js | 15 ++ .../invoking-external-callbacks-after.js | 17 ++ .../invoking-external-callbacks-before.js | 17 ++ .../side-effects-in-constructor.js | 9 + .../updating-state-from-props-after.js | 29 +++ .../updating-state-from-props-before.js | 23 +++ .../using-react-lifecycles-compat.js | 19 ++ 15 files changed, 451 insertions(+) create mode 100644 content/blog/2018-02-07-update-on-async-rendering.md create mode 100644 content/images/blog/strict-mode-unsafe-lifecycles-warning.png create mode 100644 examples/update-on-async-rendering/adding-event-listeners-after.js create mode 100644 examples/update-on-async-rendering/adding-event-listeners-before.js create mode 100644 examples/update-on-async-rendering/enabling-strict-mode.js create mode 100644 examples/update-on-async-rendering/fetching-external-data-after.js create mode 100644 examples/update-on-async-rendering/fetching-external-data-before.js create mode 100644 examples/update-on-async-rendering/initializing-state-after.js create mode 100644 examples/update-on-async-rendering/initializing-state-before.js create mode 100644 examples/update-on-async-rendering/invoking-external-callbacks-after.js create mode 100644 examples/update-on-async-rendering/invoking-external-callbacks-before.js create mode 100644 examples/update-on-async-rendering/side-effects-in-constructor.js create mode 100644 examples/update-on-async-rendering/updating-state-from-props-after.js create mode 100644 examples/update-on-async-rendering/updating-state-from-props-before.js create mode 100644 examples/update-on-async-rendering/using-react-lifecycles-compat.js diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md new file mode 100644 index 00000000000..0c7ed884d6d --- /dev/null +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -0,0 +1,171 @@ +--- +title: Update on Async Rendering +author: [bvaughn] +--- + +For the past few months, the React team has been experimenting with [asynchronous rendering](/blog/2017/09/26/react-v16.0.html#new-core-architecture), and we are very excited about the new features it enables. + +Along the way our research has shown that some of our legacy component lifecycles tend to encourage unsafe coding practices. They are: + +* `componentWillMount` +* `componentWillReceiveProps` +* `componentWillUpdate` + +Because of this, we have decided to rename the above lifecycles- (adding an "UNSAFE_" prefix)- in a future release. The plan for this is as follows: + +* **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.) +* **16.4**: Enable deprecation warning for `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate`. (Both the old lifecycle names and the new aliases will work in this release.) +* **17.0**: Remove `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate` . (Only the new "UNSAFE_" lifecycle names will work in this release.) + +In this post, we will explore some of the potential capabilities of async rendering, and we'll outline a migration plan for components that rely on the deprecated lifecycles. + +## What can Asynchronous Rendering do? + +#### With every new version, our goal is to improve the user experience of apps created with React. + +We have been fine-tuning the performance of React with every new release. However, despite what synthetic benchmarks say, we've found that the real bottleneck is generally not React itself, but the application code using it. In order to unlock the next wave of performance optimizations and new features, we need React to be smarter about when to re-render components and flush updates to the screen. + +We found that asynchronous rendering can help in several ways. For example: + +1. As users navigate within an app, newly displayed components often have asynchronous dependencies (including data, images, and code splitting). This can lead to a "cascade of spinners" as the data loads. We'd like to make it easier for product developers to express asynchronous dependencies of components- keeping the old UI "alive" for a certain period while the new UI is not "ready" yet. React could render this new UI in the background and provide a declarative way to show a spinner if it takes more than a second. +2. Fast updates within a short timeframe often cause jank because React processes each update individually. We'd like to automatically "combine" updates within a few hundred milliseconds when possible so that there is less re-rendering. +3. Some updates are inherently "less important" than others. For example, if you're writing a live-updating search filter input like [this](https://zeit.co/blog/domains-search-web#asynchronous-rendering), it is essential that the input is updated immediately (within a few milliseconds). Re-rendering the result list can be done later, and should not block the thread or cause stutter when typing. It would be nice if React had a way to mark the latter updates as having a "lower priority". +4. For UI elements like hidden popups and tabs, we'd like to be able to start pre-rendering their content when the browser isn't busy. This way, they can appear instantaneously in response to a later user interaction. However, we don't want to make the initial rendering slower, so it's essential to render such elements lazily ([when the browser is idle](https://developers.google.com/web/updates/2015/08/using-requestidlecallback)). +5. For many apps, React is not the only JavaScript on the page. It often has to coordinate with other JS libraries, server-rendered widgets, and so on. Asynchronous rendering lets React better coordinate with non-React code regarding when components are inserted into the DOM so that [the user experience is smooth](https://twitter.com/acdlite/status/909926793536094209). + +Of course, it's possible to implement some of these features today, but it's difficult. We hope to make them effortless by building them into React itself. By replacing problematic lifecycles with safer alternatives, we also hope to make it simple to write async-safe React components. + +In the next section, we'll look at how to update your existing components to prepare for the upcoming lifecycle changes. + +## Updating class components + +#### If you're an application developer, **you don't have to do anything about the deprecated methods yet**. The primary purpose of this release is to enable OSS maintainers to update their libraries in advance of any deprecation warnings. (Those warnings will be enabled with the next minor release, version 16.4.) + +However, if you'd like to start using the new component API (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's async-safe. + +### Initializing state + +This example shows a component with `setState` calls inside of `componentWillMount`: +`embed:update-on-async-rendering/initializing-state-before.js` + +The simplest refactor for this type of component is to move the state-updates to the constructor or to a property initializer, like so: +`embed:update-on-async-rendering/initializing-state-after.js` + +### Fetching external data + +Here is an example of a component that uses `componentWillMount` to fetch external data:: +`embed:update-on-async-rendering/fetching-external-data-before.js` + +The upgrade path for this is just to move data-fetching into `componentDidMount`: +`embed:update-on-async-rendering/fetching-external-data-after.js` + +> **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](https://gist.github.com/bvaughn/89700e525ff423a75ffb63b1b1e30a8f). + +### Adding event listeners (or subscriptions) + +Here is an example of a component that subscribes to an external event dispatcher when mounting: +`embed:update-on-async-rendering/adding-event-listeners-before.js` + +Unfortunately, this can cause memory leaks in async mode since rendering might be interrupted before it is committed. (In that case, `componentWillUnmount` might not be called.) The solution is to use the `componentDidMount` lifecycle instead: +`embed:update-on-async-rendering/adding-event-listeners-after.js` + +> **Note** +> +> This potential memory leak is not specific to async. The example shown above would also cause problems when rendering a component to a string. + +### 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: +`embed:update-on-async-rendering/updating-state-from-props-before.js` + +As of version 16.3, this can be done with the new `static getDerivedStateFromProps` lifecycle: +`embed:update-on-async-rendering/updating-state-from-props-after.js` + +> **Note** +> +> That the [`react-lifecycles-compat`](https://github.com/reactjs/react-lifecycles-compat) polyfill allows this new lifecycle to be used with older versions of React as well. + +### Invoking external callbacks + +Here is an example of a component that calls an external function when its internal state changes: +`embed:update-on-async-rendering/invoking-external-callbacks-before.js` + +This would not be safe to do in async mode, because the external callback might get called multiple times for a single update. Instead, the `componentDidUpdate` lifecycle should be used: +`embed:update-on-async-rendering/invoking-external-callbacks-after.js` + +## OSS maintainers + +If you're an open source maintainer, you might be asking yourself what these changes mean for your library. 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 that drops compatibility for React 16.2 and older? + +Fortunately, you do not! + +Along with the release of 16.3, we've also released a new NPM package, [`react-lifecycles-compat`](https://github.com/reactjs/react-lifecycles-compat). This package polyfills components so that the new `getDerivedStateFromProps` lifecycle will also work with older versions of React (0.14.9+). + +To use this polyfill, first add it as a dependency to your library: + +```bash +# Yarn +yarn add react-lifecycles-compat + +# NPM +npm install react-lifecycles-compat --save +``` + +Next, update your component(s) to use the new static lifecycle, `getDerivedStateFromProps`, as described above. + +Lastly, use the polyfill to make your component backwards compatible with older versions of React: +`embed:update-on-async-rendering/using-react-lifecycles-compat.js` + +## The StrictMode component + +`StrictMode` is a tool for highlighting potential problems in an application. Like `Fragment`, `StrictMode` does not render any visible UI. It simply activates additional checks and warnings for its descendants. + +> **Note** +> +> These checks are run in development mode only; **they do not impact the production build**. + +You can enable strict mode for any part of your application. For example: +`embed:update-on-async-rendering/enabling-strict-mode.js` + +In the above example, strict mode checks will *not* be run against the `Header` and `Footer` components. However, `RouteOne` and `RouteTwo`, as well as all of their descendants, will have the checks. + +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. + +### Identifying unsafe lifecycles + +As previously mentioned, certain legacy lifecycle methods are unsafe for use in async React applications. However, if your application uses third party libraries, it can be difficult to ensure that these lifecycles aren't being used. Fortunately, strict mode can help with this! + +When strict mode is enabled, React compiles a list of all class components using the unsafe lifecycles, and logs a warning message with information about these components, like so: + +![](../images/blog/strict-mode-unsafe-lifecycles-warning.png) + +Addressing the issues identified by strict mode _now_ will make it easier for you to take advantage of async rendering in future releases of React. + +### Detecting unexpected side effects + +As a general rule, side-effects should be avoided in certain class component methods (e.g. the `constructor`, `render`, etc). This is because React may invoke these methods more than once before committing, or it may invoke them without committing at all (because of an error or a higher priority interruption). Ignoring this rule can lead to a variety of problems, including memory leaks and invalid state. Unfortunately, it can be difficult to detect these problems as they are often non-deterministic. + +Strict mode can't detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following methods: + +* Class component `constructor` method +* The `render` method +* `setState` updater functions +* The static `getDerivedStateFromProps` lifecycle + +> **Note**: +> +> This only applies to development mode. **Lifecycles will not be double-invoked during production mode.** + +For example, consider the following code: +`embed:update-on-async-rendering/side-effects-in-constructor.js` + +At first glance, this code might not seem problematic. But if `SharedApplicationState.recordEvent` is not idempotent, then instantiating this component multiple times could lead to invalid application state. This sort of subtle bug might not manifest during development, or it might do so inconsistently and so be overlooked. + +By intentionally double-invoking methods like the component constructor, strict mode makes patterns like this easier to spot. diff --git a/content/images/blog/strict-mode-unsafe-lifecycles-warning.png b/content/images/blog/strict-mode-unsafe-lifecycles-warning.png new file mode 100644 index 0000000000000000000000000000000000000000..fbdeccde6bec8f2fbb8d8c8db9c4f87d7d395c82 GIT binary patch literal 53348 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 axo^$0_?r`rnMWwd>w}b%WVyIu!2buI6T3wK literal 0 HcmV?d00001 diff --git a/examples/update-on-async-rendering/adding-event-listeners-after.js b/examples/update-on-async-rendering/adding-event-listeners-after.js new file mode 100644 index 00000000000..dbff8e0e049 --- /dev/null +++ b/examples/update-on-async-rendering/adding-event-listeners-after.js @@ -0,0 +1,39 @@ +// After +class ExampleComponent extends React.Component { + // highlight-range{1-4} + state = { + subscribedValue: this.props + .dataSource.value, + }; + + // highlight-range{1-19} + 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._onSubscriptionChange + ); + + // 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._onSubscriptionChange + ); + } + + _onSubscriptionChange = subscribedValue => { + this.setState({subscribedValue}); + }; +} diff --git a/examples/update-on-async-rendering/adding-event-listeners-before.js b/examples/update-on-async-rendering/adding-event-listeners-before.js new file mode 100644 index 00000000000..9f931de22ed --- /dev/null +++ b/examples/update-on-async-rendering/adding-event-listeners-before.js @@ -0,0 +1,25 @@ +// Before +class ExampleComponent extends React.Component { + // highlight-range{1-11} + componentWillMount() { + this.setState({ + subscribedValue: this.props + .dataSource.value, + }); + + // This is not safe; it can leak! + this.props.dataSource.subscribe( + this._onSubscriptionChange + ); + } + + componentWillUnmount() { + this.props.dataSource.unsubscribe( + this._onSubscriptionChange + ); + } + + _onSubscriptionChange = subscribedValue => { + this.setState({subscribedValue}); + }; +} diff --git a/examples/update-on-async-rendering/enabling-strict-mode.js b/examples/update-on-async-rendering/enabling-strict-mode.js new file mode 100644 index 00000000000..8f12aac5715 --- /dev/null +++ b/examples/update-on-async-rendering/enabling-strict-mode.js @@ -0,0 +1,21 @@ +import React from 'react'; + +// highlight-next-line +const {StrictMode} = React; + +function ExampleApplication() { + return ( +
+
+ {/* highlight-next-line */} + + <> + + + + {/* highlight-next-line */} + +
+
+ ); +} diff --git a/examples/update-on-async-rendering/fetching-external-data-after.js b/examples/update-on-async-rendering/fetching-external-data-after.js new file mode 100644 index 00000000000..dc237168e9b --- /dev/null +++ b/examples/update-on-async-rendering/fetching-external-data-after.js @@ -0,0 +1,33 @@ +// After +class ExampleComponent extends React.Component { + // highlight-next-line + _hasUnmounted = false; + + state = { + externalData: null, + }; + + // highlight-range{1-9} + componentDidMount() { + asyncLoadData( + this.props.someId + ).then(externalData => { + if (!this._hasUnmounted) { + this.setState({externalData}); + } + }); + } + + // highlight-range{1-3} + componentWillUnmount() { + this._hasUnmounted = true; + } + + render() { + if (this.externalData === null) { + // Render loading state ... + } else { + // Render real UI ... + } + } +} diff --git a/examples/update-on-async-rendering/fetching-external-data-before.js b/examples/update-on-async-rendering/fetching-external-data-before.js new file mode 100644 index 00000000000..bd8c430a624 --- /dev/null +++ b/examples/update-on-async-rendering/fetching-external-data-before.js @@ -0,0 +1,23 @@ +// Before +class ExampleComponent extends React.Component { + state = { + externalData: null, + }; + + // highlight-range{1-7} + componentWillMount() { + asyncLoadData( + this.props.someId + ).then(externalData => + this.setState({externalData}) + ); + } + + render() { + if (this.externalData === null) { + // Render loading state ... + } else { + // Render real UI ... + } + } +} diff --git a/examples/update-on-async-rendering/initializing-state-after.js b/examples/update-on-async-rendering/initializing-state-after.js new file mode 100644 index 00000000000..a437c339453 --- /dev/null +++ b/examples/update-on-async-rendering/initializing-state-after.js @@ -0,0 +1,10 @@ +// After +class ExampleComponent extends React.Component { + // highlight-range{1-6} + state = { + count: 0, + derivedValue: computeDerivedValue( + this.props + ), + }; +} diff --git a/examples/update-on-async-rendering/initializing-state-before.js b/examples/update-on-async-rendering/initializing-state-before.js new file mode 100644 index 00000000000..bd080ea1379 --- /dev/null +++ b/examples/update-on-async-rendering/initializing-state-before.js @@ -0,0 +1,15 @@ +// Before +class ExampleComponent extends React.Component { + // highlight-next-line + state = {}; + + // highlight-range{1-8} + componentWillMount() { + this.setState({ + count: 0, + derivedValue: computeDerivedValue( + this.props + ), + }); + } +} diff --git a/examples/update-on-async-rendering/invoking-external-callbacks-after.js b/examples/update-on-async-rendering/invoking-external-callbacks-after.js new file mode 100644 index 00000000000..20ee43738bb --- /dev/null +++ b/examples/update-on-async-rendering/invoking-external-callbacks-after.js @@ -0,0 +1,17 @@ +// After +class ExampleComponent extends React.Component { + // highlight-range{1-13} + componentDidUpdate( + prevProps, + prevState + ) { + if ( + this.state.someStatefulValue !== + prevState.someStatefulValue + ) { + this.props.onChange( + this.state.someStatefulValue + ); + } + } +} diff --git a/examples/update-on-async-rendering/invoking-external-callbacks-before.js b/examples/update-on-async-rendering/invoking-external-callbacks-before.js new file mode 100644 index 00000000000..9007b4794dc --- /dev/null +++ b/examples/update-on-async-rendering/invoking-external-callbacks-before.js @@ -0,0 +1,17 @@ +// Before +class ExampleComponent extends React.Component { + // highlight-range{1-13} + componentWillUpdate( + nextProps, + nextState + ) { + if ( + this.state.someStatefulValue !== + nextState.someStatefulValue + ) { + nextProps.onChange( + nextState.someStatefulValue + ); + } + } +} diff --git a/examples/update-on-async-rendering/side-effects-in-constructor.js b/examples/update-on-async-rendering/side-effects-in-constructor.js new file mode 100644 index 00000000000..004887c6517 --- /dev/null +++ b/examples/update-on-async-rendering/side-effects-in-constructor.js @@ -0,0 +1,9 @@ +class TopLevelRoute extends React.Component { + constructor(props) { + super(props); + + SharedApplicationState.recordEvent( + 'ExampleComponent' + ); + } +} diff --git a/examples/update-on-async-rendering/updating-state-from-props-after.js b/examples/update-on-async-rendering/updating-state-from-props-after.js new file mode 100644 index 00000000000..7727a171f01 --- /dev/null +++ b/examples/update-on-async-rendering/updating-state-from-props-after.js @@ -0,0 +1,29 @@ +// After +class ExampleComponent extends React.Component { + // highlight-range{1-3} + // Initialize state in constructor, + // Or with a property initializer. + state = {}; + + // highlight-range{1-20} + static getDerivedStateFromProps( + nextProps, + prevState + ) { + if ( + prevState.someMirroredValue !== + nextProps.someValue + ) { + return { + derivedData: computeDerivedState( + nextProps + ), + someMirroredValue: + nextProps.someValue, + }; + } + + // Return null to indicate no change to state. + return null; + } +} diff --git a/examples/update-on-async-rendering/updating-state-from-props-before.js b/examples/update-on-async-rendering/updating-state-from-props-before.js new file mode 100644 index 00000000000..7c59407f155 --- /dev/null +++ b/examples/update-on-async-rendering/updating-state-from-props-before.js @@ -0,0 +1,23 @@ +// Before +class ExampleComponent extends React.Component { + // highlight-range{1-5} + state = { + derivedData: computeDerivedState( + this.props + ), + }; + + // highlight-range{1-12} + componentWillReceiveProps(nextProps) { + if ( + this.props.someValue !== + nextProps.someValue + ) { + this.setState({ + derivedData: computeDerivedState( + nextProps + ), + }); + } + } +} diff --git a/examples/update-on-async-rendering/using-react-lifecycles-compat.js b/examples/update-on-async-rendering/using-react-lifecycles-compat.js new file mode 100644 index 00000000000..49c1bfd0d29 --- /dev/null +++ b/examples/update-on-async-rendering/using-react-lifecycles-compat.js @@ -0,0 +1,19 @@ +import React from 'react'; +// highlight-next-line +import polyfill from 'react-lifecycles-compat'; + +class ExampleComponent extends React.Component { + // highlight-next-line + static getDerivedStateFromProps( + nextProps, + prevState + ) { + /* ... */ + } +} + +// Polyfill your component to work with older versions of React: +// highlight-next-line +polyfill(ExampleComponent); + +export default ExampleComponent; From e298b47057ce0fd0f00d32bc93489a65a4d62d95 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 6 Feb 2018 13:53:24 -0800 Subject: [PATCH 02/61] Wordsmithing --- .../2018-02-07-update-on-async-rendering.md | 30 ++++++++++--------- .../using-react-lifecycles-compat.js | 2 +- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index 0c7ed884d6d..086939cfd55 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -11,7 +11,7 @@ Along the way our research has shown that some of our legacy component lifecycle * `componentWillReceiveProps` * `componentWillUpdate` -Because of this, we have decided to rename the above lifecycles- (adding an "UNSAFE_" prefix)- in a future release. The plan for this is as follows: +Because of this, we have decided to rename these lifecycles—(adding an "UNSAFE_" prefix)—in a future release. The plan for this is as follows: * **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.) * **16.4**: Enable deprecation warning for `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate`. (Both the old lifecycle names and the new aliases will work in this release.) @@ -39,7 +39,7 @@ In the next section, we'll look at how to update your existing components to pre ## Updating class components -#### If you're an application developer, **you don't have to do anything about the deprecated methods yet**. The primary purpose of this release is to enable OSS maintainers to update their libraries in advance of any deprecation warnings. (Those warnings will be enabled with the next minor release, version 16.4.) +#### If you're an application developer, **you don't have to do anything about the deprecated methods yet**. The primary purpose of this update (v16.3) is to enable OSS maintainers to update their libraries in advance of any deprecation warnings. Those warnings will be enabled with the next minor release, v16.4. However, if you'd like to start using the new component API (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's async-safe. @@ -56,7 +56,9 @@ The simplest refactor for this type of component is to move the state-updates to Here is an example of a component that uses `componentWillMount` to fetch external data:: `embed:update-on-async-rendering/fetching-external-data-before.js` -The upgrade path for this is just to move data-fetching into `componentDidMount`: +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, or executed unnecessarily). + +The upgrade path for this is to move data-fetching into `componentDidMount`: `embed:update-on-async-rendering/fetching-external-data-after.js` > **Note** @@ -68,36 +70,36 @@ The upgrade path for this is just to move data-fetching into `componentDidMount` Here is an example of a component that subscribes to an external event dispatcher when mounting: `embed:update-on-async-rendering/adding-event-listeners-before.js` -Unfortunately, this can cause memory leaks in async mode since rendering might be interrupted before it is committed. (In that case, `componentWillUnmount` might not be called.) The solution is to use the `componentDidMount` lifecycle instead: -`embed:update-on-async-rendering/adding-event-listeners-after.js` +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). -> **Note** -> -> This potential memory leak is not specific to async. The example shown above would also cause problems when rendering a component to a string. +The solution is to use the `componentDidMount` lifecycle instead: +`embed:update-on-async-rendering/adding-event-listeners-after.js` ### 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: `embed:update-on-async-rendering/updating-state-from-props-before.js` -As of version 16.3, this can be done with the new `static getDerivedStateFromProps` lifecycle: +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 has been deprecated. + +As of version 16.3, the recommended way to update `state` in response to `props` changes is using the new `static getDerivedStateFromProps` lifecycle: `embed:update-on-async-rendering/updating-state-from-props-after.js` > **Note** > -> That the [`react-lifecycles-compat`](https://github.com/reactjs/react-lifecycles-compat) polyfill allows this new lifecycle to be used with older versions of React as well. +> The [`react-lifecycles-compat`](https://github.com/reactjs/react-lifecycles-compat) polyfill allows this new lifecycle to be used with older versions of React as well. This can be helpful if you're writing a shared component that is intended for use with multiple versions of React. ### Invoking external callbacks Here is an example of a component that calls an external function when its internal state changes: `embed:update-on-async-rendering/invoking-external-callbacks-before.js` -This would not be safe to do in async mode, because the external callback might get called multiple times for a single update. Instead, the `componentDidUpdate` lifecycle should be used: +This would not be safe to do 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: `embed:update-on-async-rendering/invoking-external-callbacks-after.js` ## OSS maintainers -If you're an open source maintainer, you might be asking yourself what these changes mean for your library. 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 that drops compatibility for React 16.2 and older? +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! @@ -113,7 +115,7 @@ yarn add react-lifecycles-compat npm install react-lifecycles-compat --save ``` -Next, update your component(s) to use the new static lifecycle, `getDerivedStateFromProps`, as described above. +Next, update your components to use the new static lifecycle, `getDerivedStateFromProps`, as described above. Lastly, use the polyfill to make your component backwards compatible with older versions of React: `embed:update-on-async-rendering/using-react-lifecycles-compat.js` @@ -124,7 +126,7 @@ Lastly, use the polyfill to make your component backwards compatible with older > **Note** > -> These checks are run in development mode only; **they do not impact the production build**. +> Strict mode checks are run in development mode only; **they do not impact the production build**. You can enable strict mode for any part of your application. For example: `embed:update-on-async-rendering/enabling-strict-mode.js` diff --git a/examples/update-on-async-rendering/using-react-lifecycles-compat.js b/examples/update-on-async-rendering/using-react-lifecycles-compat.js index 49c1bfd0d29..0913181b5df 100644 --- a/examples/update-on-async-rendering/using-react-lifecycles-compat.js +++ b/examples/update-on-async-rendering/using-react-lifecycles-compat.js @@ -8,7 +8,7 @@ class ExampleComponent extends React.Component { nextProps, prevState ) { - /* ... */ + // Your state update logic here ... } } From 9ff0f122aff29af7c00facc14b50f47005b65107 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 6 Feb 2018 13:59:52 -0800 Subject: [PATCH 03/61] Split StrictMode into its own top level docs page --- content/blog/2018-02-07-strict-mode.md | 54 +++++++++++++++++ .../2018-02-07-update-on-async-rendering.md | 58 +------------------ 2 files changed, 57 insertions(+), 55 deletions(-) create mode 100644 content/blog/2018-02-07-strict-mode.md diff --git a/content/blog/2018-02-07-strict-mode.md b/content/blog/2018-02-07-strict-mode.md new file mode 100644 index 00000000000..0e672a07a81 --- /dev/null +++ b/content/blog/2018-02-07-strict-mode.md @@ -0,0 +1,54 @@ +--- +title: Strict mode +author: [bvaughn] +--- + +`StrictMode` is a tool for highlighting potential problems in an application. Like `Fragment`, `StrictMode` does not render any visible UI. It simply activates additional checks and warnings for its descendants. + +> Note: +> +> Strict mode checks are run in development mode only; _they do not impact the production build_. + +You can enable strict mode for any part of your application. For example: +`embed:update-on-async-rendering/enabling-strict-mode.js` + +In the above example, strict mode checks will *not* be run against the `Header` and `Footer` components. However, `RouteOne` and `RouteTwo`, as well as all of their descendants, will have the checks. + +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. + +### Identifying unsafe lifecycles + +As previously mentioned, certain legacy lifecycle methods are unsafe for use in async React applications. However, if your application uses third party libraries, it can be difficult to ensure that these lifecycles aren't being used. Fortunately, strict mode can help with this! + +When strict mode is enabled, React compiles a list of all class components using the unsafe lifecycles, and logs a warning message with information about these components, like so: + +![](../images/blog/strict-mode-unsafe-lifecycles-warning.png) + +Addressing the issues identified by strict mode _now_ will make it easier for you to take advantage of async rendering in future releases of React. + +### Detecting unexpected side effects + +As a general rule, side-effects should be avoided in certain class component methods (e.g. the `constructor`, `render`, etc). This is because React may invoke these methods more than once before committing, or it may invoke them without committing at all (because of an error or a higher priority interruption). Ignoring this rule can lead to a variety of problems, including memory leaks and invalid state. Unfortunately, it can be difficult to detect these problems as they are often non-deterministic. + +Strict mode can't automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following methods: + +* Class component `constructor` method +* The `render` method +* `setState` updater functions +* The static `getDerivedStateFromProps` lifecycle + +> Note: +> +> This only applies to development mode. _Lifecycles will not be double-invoked in production mode._ + +For example, consider the following code: +`embed:update-on-async-rendering/side-effects-in-constructor.js` + +At first glance, this code might not seem problematic. But if `SharedApplicationState.recordEvent` is not idempotent, then instantiating this component multiple times could lead to invalid application state. This sort of subtle bug might not manifest during development, or it might do so inconsistently and so be overlooked. + +By intentionally double-invoking methods like the component constructor, strict mode makes patterns like this easier to spot. diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index 086939cfd55..7066201fe9c 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -61,7 +61,7 @@ The above code is problematic for both server rendering (where the external data The upgrade path for this is to move data-fetching into `componentDidMount`: `embed:update-on-async-rendering/fetching-external-data-after.js` -> **Note** +> 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](https://gist.github.com/bvaughn/89700e525ff423a75ffb63b1b1e30a8f). @@ -85,7 +85,7 @@ Although the above code is not problematic in itself, the `componentWillReceiveP As of version 16.3, the recommended way to update `state` in response to `props` changes is using the new `static getDerivedStateFromProps` lifecycle: `embed:update-on-async-rendering/updating-state-from-props-after.js` -> **Note** +> Note: > > The [`react-lifecycles-compat`](https://github.com/reactjs/react-lifecycles-compat) polyfill allows this new lifecycle to be used with older versions of React as well. This can be helpful if you're writing a shared component that is intended for use with multiple versions of React. @@ -118,56 +118,4 @@ npm install react-lifecycles-compat --save Next, update your components to use the new static lifecycle, `getDerivedStateFromProps`, as described above. Lastly, use the polyfill to make your component backwards compatible with older versions of React: -`embed:update-on-async-rendering/using-react-lifecycles-compat.js` - -## The StrictMode component - -`StrictMode` is a tool for highlighting potential problems in an application. Like `Fragment`, `StrictMode` does not render any visible UI. It simply activates additional checks and warnings for its descendants. - -> **Note** -> -> Strict mode checks are run in development mode only; **they do not impact the production build**. - -You can enable strict mode for any part of your application. For example: -`embed:update-on-async-rendering/enabling-strict-mode.js` - -In the above example, strict mode checks will *not* be run against the `Header` and `Footer` components. However, `RouteOne` and `RouteTwo`, as well as all of their descendants, will have the checks. - -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. - -### Identifying unsafe lifecycles - -As previously mentioned, certain legacy lifecycle methods are unsafe for use in async React applications. However, if your application uses third party libraries, it can be difficult to ensure that these lifecycles aren't being used. Fortunately, strict mode can help with this! - -When strict mode is enabled, React compiles a list of all class components using the unsafe lifecycles, and logs a warning message with information about these components, like so: - -![](../images/blog/strict-mode-unsafe-lifecycles-warning.png) - -Addressing the issues identified by strict mode _now_ will make it easier for you to take advantage of async rendering in future releases of React. - -### Detecting unexpected side effects - -As a general rule, side-effects should be avoided in certain class component methods (e.g. the `constructor`, `render`, etc). This is because React may invoke these methods more than once before committing, or it may invoke them without committing at all (because of an error or a higher priority interruption). Ignoring this rule can lead to a variety of problems, including memory leaks and invalid state. Unfortunately, it can be difficult to detect these problems as they are often non-deterministic. - -Strict mode can't detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following methods: - -* Class component `constructor` method -* The `render` method -* `setState` updater functions -* The static `getDerivedStateFromProps` lifecycle - -> **Note**: -> -> This only applies to development mode. **Lifecycles will not be double-invoked during production mode.** - -For example, consider the following code: -`embed:update-on-async-rendering/side-effects-in-constructor.js` - -At first glance, this code might not seem problematic. But if `SharedApplicationState.recordEvent` is not idempotent, then instantiating this component multiple times could lead to invalid application state. This sort of subtle bug might not manifest during development, or it might do so inconsistently and so be overlooked. - -By intentionally double-invoking methods like the component constructor, strict mode makes patterns like this easier to spot. +`embed:update-on-async-rendering/using-react-lifecycles-compat.js` \ No newline at end of file From 0674c34f0ae88bafe4e873f653eee67a6885da4f Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 6 Feb 2018 14:05:57 -0800 Subject: [PATCH 04/61] Added string ref section to strict mode page --- content/blog/2018-02-07-strict-mode.md | 17 ++++++++++++++++- examples/16-3-release-blog-create-ref.js | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 examples/16-3-release-blog-create-ref.js diff --git a/content/blog/2018-02-07-strict-mode.md b/content/blog/2018-02-07-strict-mode.md index 0e672a07a81..e46b5ab3c80 100644 --- a/content/blog/2018-02-07-strict-mode.md +++ b/content/blog/2018-02-07-strict-mode.md @@ -15,7 +15,7 @@ You can enable strict mode for any part of your application. For example: In the above example, strict mode checks will *not* be run against the `Header` and `Footer` components. However, `RouteOne` and `RouteTwo`, as well as all of their descendants, will have the checks. In version 16.3, `StrictMode` helps with: -* Identifying components with unsafe lifecycles +* [Identifying components with unsafe lifecycles](#identifying-unsafe-lifecycles) * Warning about legacy string ref API usage * Detecting unexpected side effects @@ -31,6 +31,21 @@ When strict mode is enabled, React compiles a list of all class components using Addressing the issues identified by strict mode _now_ will make it easier for you to take advantage of async rendering in future releases of React. +### Warning about legacy string ref API usage + +Previously, React provided two ways for 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](https://github.com/facebook/react/issues/1373) and so our official recomendation was to [use the callback form instead](https://reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs). + +Version 16.3 adds a new option for managing refs that offers the convenience of a string ref without any of the downsides: +`embed:16-3-release-blog-create-ref.js` + +> **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.](#) + ### Detecting unexpected side effects As a general rule, side-effects should be avoided in certain class component methods (e.g. the `constructor`, `render`, etc). This is because React may invoke these methods more than once before committing, or it may invoke them without committing at all (because of an error or a higher priority interruption). Ignoring this rule can lead to a variety of problems, including memory leaks and invalid state. Unfortunately, it can be difficult to detect these problems as they are often non-deterministic. diff --git a/examples/16-3-release-blog-create-ref.js b/examples/16-3-release-blog-create-ref.js new file mode 100644 index 00000000000..410833028c2 --- /dev/null +++ b/examples/16-3-release-blog-create-ref.js @@ -0,0 +1,19 @@ +class MyComponent extends React.Component { + // highlight-next-line + divRef = React.createRef(); + + render() { + // highlight-range{4} + return ( + + ); + } + + componentDidMount() { + // highlight-next-line + this.divRef.value.focus(); + } +} \ No newline at end of file From cdb4b9ee14a9aaee105c714b37d81019e4437b18 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 6 Feb 2018 14:06:49 -0800 Subject: [PATCH 05/61] Added TOC header links to Strict Mode page --- content/blog/2018-02-07-strict-mode.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/blog/2018-02-07-strict-mode.md b/content/blog/2018-02-07-strict-mode.md index e46b5ab3c80..941bce27d5c 100644 --- a/content/blog/2018-02-07-strict-mode.md +++ b/content/blog/2018-02-07-strict-mode.md @@ -16,8 +16,8 @@ In the above example, strict mode checks will *not* be run against the `Header` In version 16.3, `StrictMode` helps with: * [Identifying components with unsafe lifecycles](#identifying-unsafe-lifecycles) -* Warning about legacy string ref API usage -* Detecting unexpected side effects +* [Warning about legacy string ref API usage](#warning-about-legacy-string-ref-api-usage) +* [Detecting unexpected side effects](#detecting-unexpected-side-effects) Additional functionality will be added with future releases of React. From 289a2da039f3b631e3540ebfa837a21987be9a60 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 6 Feb 2018 14:09:47 -0800 Subject: [PATCH 06/61] Prettier --- examples/16-3-release-blog-create-ref.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/16-3-release-blog-create-ref.js b/examples/16-3-release-blog-create-ref.js index 410833028c2..f189e0d0667 100644 --- a/examples/16-3-release-blog-create-ref.js +++ b/examples/16-3-release-blog-create-ref.js @@ -16,4 +16,4 @@ class MyComponent extends React.Component { // highlight-next-line this.divRef.value.focus(); } -} \ No newline at end of file +} From 99fedeaae1c424f7f12b8f92ade855e7bd1b776d Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 6 Feb 2018 14:51:09 -0800 Subject: [PATCH 07/61] Wording changes in response to PR feedback --- content/blog/2018-02-07-update-on-async-rendering.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index 7066201fe9c..0548a57fc43 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -27,9 +27,9 @@ We have been fine-tuning the performance of React with every new release. Howeve We found that asynchronous rendering can help in several ways. For example: -1. As users navigate within an app, newly displayed components often have asynchronous dependencies (including data, images, and code splitting). This can lead to a "cascade of spinners" as the data loads. We'd like to make it easier for product developers to express asynchronous dependencies of components- keeping the old UI "alive" for a certain period while the new UI is not "ready" yet. React could render this new UI in the background and provide a declarative way to show a spinner if it takes more than a second. +1. As users navigate within an app, newly displayed components often have asynchronous dependencies (including data, images, and code splitting). This leads to a lot of boilerplate code managing data fetching and displaying the loading states. It can also lead to a “cascade of spinners” as the data loads, causing DOM reflows and janky user experience. We'd like to make it easier for product developers to express asynchronous dependencies of components- keeping the old UI "alive" for a certain period while the new UI is not "ready" yet. React could render this new UI in the background and provide a declarative way to show a loading indicator if it takes more than a second. 2. Fast updates within a short timeframe often cause jank because React processes each update individually. We'd like to automatically "combine" updates within a few hundred milliseconds when possible so that there is less re-rendering. -3. Some updates are inherently "less important" than others. For example, if you're writing a live-updating search filter input like [this](https://zeit.co/blog/domains-search-web#asynchronous-rendering), it is essential that the input is updated immediately (within a few milliseconds). Re-rendering the result list can be done later, and should not block the thread or cause stutter when typing. It would be nice if React had a way to mark the latter updates as having a "lower priority". +3. Some updates are inherently less important than others. For example, if you're writing a live-updating search filter input like [this](https://zeit.co/blog/domains-search-web#asynchronous-rendering), it is essential that the input is updated immediately (within a few milliseconds). Re-rendering the result list can be done later, and should not block the thread or cause stutter when typing. It would be nice if React had a way to mark the latter updates as having a lower priority. (Note that even debouncing the input doesn't help because if the rendering is synchronous—like in React today—a keystroke can't interrupt the rendering if it already started. Asynchronous rendering solves this by splitting rendering into small chunks that can be paused and later restarted.) 4. For UI elements like hidden popups and tabs, we'd like to be able to start pre-rendering their content when the browser isn't busy. This way, they can appear instantaneously in response to a later user interaction. However, we don't want to make the initial rendering slower, so it's essential to render such elements lazily ([when the browser is idle](https://developers.google.com/web/updates/2015/08/using-requestidlecallback)). 5. For many apps, React is not the only JavaScript on the page. It often has to coordinate with other JS libraries, server-rendered widgets, and so on. Asynchronous rendering lets React better coordinate with non-React code regarding when components are inserted into the DOM so that [the user experience is smooth](https://twitter.com/acdlite/status/909926793536094209). @@ -39,7 +39,7 @@ In the next section, we'll look at how to update your existing components to pre ## Updating class components -#### If you're an application developer, **you don't have to do anything about the deprecated methods yet**. The primary purpose of this update (v16.3) is to enable OSS maintainers to update their libraries in advance of any deprecation warnings. Those warnings will be enabled with the next minor release, v16.4. +#### If you're an application developer, **you don't have to do anything about the deprecated methods yet**. The primary purpose of this update (v16.3) is to enable open source project maintainers to update their libraries in advance of any deprecation warnings. Those warnings will be enabled with the next minor release, v16.4. However, if you'd like to start using the new component API (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's async-safe. @@ -72,7 +72,9 @@ Here is an example of a component that subscribes to an external event dispatche 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). -The solution is to use the `componentDidMount` lifecycle instead: +People often assume that `componentWillMount` and `componentWillUnmount` are 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 (or subscriptions) is to use the `componentDidMount` lifecycle: `embed:update-on-async-rendering/adding-event-listeners-after.js` ### Updating `state` based on `props` @@ -97,7 +99,7 @@ Here is an example of a component that calls an external function when its inter This would not be safe to do 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: `embed:update-on-async-rendering/invoking-external-callbacks-after.js` -## OSS maintainers +## 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? From 49464f77e37c1eac3671e04f8d6d96b9884d8a6f Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 6 Feb 2018 14:53:42 -0800 Subject: [PATCH 08/61] Removed some unnecessary parens --- content/blog/2018-02-07-update-on-async-rendering.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index 0548a57fc43..362c954d946 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -72,9 +72,9 @@ Here is an example of a component that subscribes to an external event dispatche 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 paired, but that is not guaranteed. Only once `componentDidMount` has been called does React guarantee that `componentWillUnmount` will later be called (for clean up). +People often assume that `componentWillMount` and `componentWillUnmount` are 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 (or subscriptions) is to use the `componentDidMount` lifecycle: +For this reason, the recommended way to add listeners/subscriptions is to use the `componentDidMount` lifecycle: `embed:update-on-async-rendering/adding-event-listeners-after.js` ### Updating `state` based on `props` From fe6b1333f791b3f7b9a953be650ea9b049a4df62 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 6 Feb 2018 14:58:17 -0800 Subject: [PATCH 09/61] Add Dan as an author to "Update on Async" --- content/blog/2018-02-07-update-on-async-rendering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index 362c954d946..266deda94cf 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -1,6 +1,6 @@ --- title: Update on Async Rendering -author: [bvaughn] +author: [bvaughn, gaearon] --- For the past few months, the React team has been experimenting with [asynchronous rendering](/blog/2017/09/26/react-v16.0.html#new-core-architecture), and we are very excited about the new features it enables. From 1314117e42b1337297503c81744d9cf58e6ea289 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 6 Feb 2018 21:23:46 -0800 Subject: [PATCH 10/61] Wording improvements in response to Dan's feedback --- .../2018-02-07-update-on-async-rendering.md | 18 ++++++++++++------ .../initializing-state-after.js | 8 +++----- .../initializing-state-before.js | 9 +++------ .../invoking-external-callbacks-after.js | 3 ++- .../invoking-external-callbacks-before.js | 3 ++- .../updating-state-from-props-after.js | 18 +++++++++--------- .../updating-state-from-props-before.js | 15 ++++++--------- 7 files changed, 37 insertions(+), 37 deletions(-) diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index 266deda94cf..7ae13831df6 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -11,15 +11,15 @@ Along the way our research has shown that some of our legacy component lifecycle * `componentWillReceiveProps` * `componentWillUpdate` -Because of this, we have decided to rename these lifecycles—(adding an "UNSAFE_" prefix)—in a future release. The plan for this is as follows: +Because of this, we are adding an "UNSAFE_" prefix to these lifecycles in a future release. React [follows semantic versioning](/blog/2016/02/19/new-versioning-scheme.html), so the migration path is gradual: * **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.) * **16.4**: Enable deprecation warning for `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate`. (Both the old lifecycle names and the new aliases will work in this release.) * **17.0**: Remove `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate` . (Only the new "UNSAFE_" lifecycle names will work in this release.) -In this post, we will explore some of the potential capabilities of async rendering, and we'll outline a migration plan for components that rely on the deprecated lifecycles. +In this post, we will explore some of the potential capabilities of async rendering, and we'll outline a migration plan for components that rely on these legacy lifecycles. -## What can Asynchronous Rendering do? +## What can asynchronous rendering do? #### With every new version, our goal is to improve the user experience of apps created with React. @@ -27,7 +27,7 @@ We have been fine-tuning the performance of React with every new release. Howeve We found that asynchronous rendering can help in several ways. For example: -1. As users navigate within an app, newly displayed components often have asynchronous dependencies (including data, images, and code splitting). This leads to a lot of boilerplate code managing data fetching and displaying the loading states. It can also lead to a “cascade of spinners” as the data loads, causing DOM reflows and janky user experience. We'd like to make it easier for product developers to express asynchronous dependencies of components- keeping the old UI "alive" for a certain period while the new UI is not "ready" yet. React could render this new UI in the background and provide a declarative way to show a loading indicator if it takes more than a second. +1. As users navigate within an app, newly displayed components often have asynchronous dependencies (including data, images, and code splitting). This leads to a lot of boilerplate code managing data fetching and displaying the loading states. It can also lead to a "cascade of spinners" as the data loads, causing DOM reflows and janky user experience. We'd like to make it easier for product developers to express asynchronous dependencies of components- keeping the old UI "alive" for a certain period while the new UI is not "ready" yet. React could render this new UI in the background and provide a declarative way to show a loading indicator if it takes more than a second. 2. Fast updates within a short timeframe often cause jank because React processes each update individually. We'd like to automatically "combine" updates within a few hundred milliseconds when possible so that there is less re-rendering. 3. Some updates are inherently less important than others. For example, if you're writing a live-updating search filter input like [this](https://zeit.co/blog/domains-search-web#asynchronous-rendering), it is essential that the input is updated immediately (within a few milliseconds). Re-rendering the result list can be done later, and should not block the thread or cause stutter when typing. It would be nice if React had a way to mark the latter updates as having a lower priority. (Note that even debouncing the input doesn't help because if the rendering is synchronous—like in React today—a keystroke can't interrupt the rendering if it already started. Asynchronous rendering solves this by splitting rendering into small chunks that can be paused and later restarted.) 4. For UI elements like hidden popups and tabs, we'd like to be able to start pre-rendering their content when the browser isn't busy. This way, they can appear instantaneously in response to a later user interaction. However, we don't want to make the initial rendering slower, so it's essential to render such elements lazily ([when the browser is idle](https://developers.google.com/web/updates/2015/08/using-requestidlecallback)). @@ -41,7 +41,7 @@ In the next section, we'll look at how to update your existing components to pre #### If you're an application developer, **you don't have to do anything about the deprecated methods yet**. The primary purpose of this update (v16.3) is to enable open source project maintainers to update their libraries in advance of any deprecation warnings. Those warnings will be enabled with the next minor release, v16.4. -However, if you'd like to start using the new component API (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's async-safe. +However, if you'd like to start using the new component API (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's async-safe. ### Initializing state @@ -96,9 +96,15 @@ As of version 16.3, the recommended way to update `state` in response to `props` Here is an example of a component that calls an external function when its internal state changes: `embed:update-on-async-rendering/invoking-external-callbacks-before.js` -This would not be safe to do 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: +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 unavoidable (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: `embed:update-on-async-rendering/invoking-external-callbacks-after.js` +## 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](https://github.com/reactjs/reactjs.org/issues/new) 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? diff --git a/examples/update-on-async-rendering/initializing-state-after.js b/examples/update-on-async-rendering/initializing-state-after.js index a437c339453..8bea2886fe3 100644 --- a/examples/update-on-async-rendering/initializing-state-after.js +++ b/examples/update-on-async-rendering/initializing-state-after.js @@ -1,10 +1,8 @@ // After class ExampleComponent extends React.Component { - // highlight-range{1-6} + // highlight-range{1-4} state = { - count: 0, - derivedValue: computeDerivedValue( - this.props - ), + isScrollingDown: false, + lastRow: this.props.currentRow, }; } diff --git a/examples/update-on-async-rendering/initializing-state-before.js b/examples/update-on-async-rendering/initializing-state-before.js index bd080ea1379..9dd5c009d0b 100644 --- a/examples/update-on-async-rendering/initializing-state-before.js +++ b/examples/update-on-async-rendering/initializing-state-before.js @@ -1,15 +1,12 @@ // Before class ExampleComponent extends React.Component { - // highlight-next-line state = {}; - // highlight-range{1-8} + // highlight-range{1-6} componentWillMount() { this.setState({ - count: 0, - derivedValue: computeDerivedValue( - this.props - ), + isScrollingDown: false, + lastRow: this.props.currentRow, }); } } diff --git a/examples/update-on-async-rendering/invoking-external-callbacks-after.js b/examples/update-on-async-rendering/invoking-external-callbacks-after.js index 20ee43738bb..57a3ad5de02 100644 --- a/examples/update-on-async-rendering/invoking-external-callbacks-after.js +++ b/examples/update-on-async-rendering/invoking-external-callbacks-after.js @@ -1,10 +1,11 @@ // After class ExampleComponent extends React.Component { - // highlight-range{1-13} + // highlight-next-line componentDidUpdate( prevProps, prevState ) { + // highlight-range{1-8} if ( this.state.someStatefulValue !== prevState.someStatefulValue diff --git a/examples/update-on-async-rendering/invoking-external-callbacks-before.js b/examples/update-on-async-rendering/invoking-external-callbacks-before.js index 9007b4794dc..3fbc002c0b2 100644 --- a/examples/update-on-async-rendering/invoking-external-callbacks-before.js +++ b/examples/update-on-async-rendering/invoking-external-callbacks-before.js @@ -1,10 +1,11 @@ // Before class ExampleComponent extends React.Component { - // highlight-range{1-13} + // highlight-next-line componentWillUpdate( nextProps, nextState ) { + // highlight-range{1-8} if ( this.state.someStatefulValue !== nextState.someStatefulValue diff --git a/examples/update-on-async-rendering/updating-state-from-props-after.js b/examples/update-on-async-rendering/updating-state-from-props-after.js index 7727a171f01..d155996178e 100644 --- a/examples/update-on-async-rendering/updating-state-from-props-after.js +++ b/examples/update-on-async-rendering/updating-state-from-props-after.js @@ -1,25 +1,25 @@ // After class ExampleComponent extends React.Component { - // highlight-range{1-3} // Initialize state in constructor, // Or with a property initializer. + // highlight-next-line state = {}; - // highlight-range{1-20} + // highlight-next-line static getDerivedStateFromProps( nextProps, prevState ) { + // highlight-range{1-11} if ( - prevState.someMirroredValue !== - nextProps.someValue + nextProps.currentRow !== + prevState.lastRow ) { return { - derivedData: computeDerivedState( - nextProps - ), - someMirroredValue: - nextProps.someValue, + lastRow: nextProps.currentRow, + isScrollingDown: + nextProps.currentRow > + prevState.lastRow, }; } diff --git a/examples/update-on-async-rendering/updating-state-from-props-before.js b/examples/update-on-async-rendering/updating-state-from-props-before.js index 7c59407f155..0e75ef38318 100644 --- a/examples/update-on-async-rendering/updating-state-from-props-before.js +++ b/examples/update-on-async-rendering/updating-state-from-props-before.js @@ -1,22 +1,19 @@ // Before class ExampleComponent extends React.Component { - // highlight-range{1-5} state = { - derivedData: computeDerivedState( - this.props - ), + isScrollingDown: false, }; // highlight-range{1-12} componentWillReceiveProps(nextProps) { if ( - this.props.someValue !== - nextProps.someValue + this.props.currentRow !== + nextProps.currentRow ) { this.setState({ - derivedData: computeDerivedState( - nextProps - ), + isScrollingDown: + nextProps.currentRow > + this.props.currentRow, }); } } From f005c0420692af5c7fa94fc0cd03ec49636c2755 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 7 Feb 2018 08:05:27 -0800 Subject: [PATCH 11/61] Wordsmithing --- content/blog/2018-02-07-update-on-async-rendering.md | 2 +- .../update-on-async-rendering/initializing-state-after.js | 4 ++-- .../update-on-async-rendering/initializing-state-before.js | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index 7ae13831df6..fcc73675c41 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -27,7 +27,7 @@ We have been fine-tuning the performance of React with every new release. Howeve We found that asynchronous rendering can help in several ways. For example: -1. As users navigate within an app, newly displayed components often have asynchronous dependencies (including data, images, and code splitting). This leads to a lot of boilerplate code managing data fetching and displaying the loading states. It can also lead to a "cascade of spinners" as the data loads, causing DOM reflows and janky user experience. We'd like to make it easier for product developers to express asynchronous dependencies of components- keeping the old UI "alive" for a certain period while the new UI is not "ready" yet. React could render this new UI in the background and provide a declarative way to show a loading indicator if it takes more than a second. +1. As users navigate within an app, newly displayed components often have asynchronous dependencies (including data, images, and code splitting). This leads to a lot of boilerplate code managing data fetching and displaying the loading states. It can also lead to a "cascade of spinners" as the data loads, causing DOM reflows and janky user experience. We'd like to make it easier for product developers to express asynchronous dependencies of components. React could keep the old UI "alive" and interactive for a certain period while the updated UI is not ready yet, and provide a declarative way to show a loading indicator if it takes more than a second. 2. Fast updates within a short timeframe often cause jank because React processes each update individually. We'd like to automatically "combine" updates within a few hundred milliseconds when possible so that there is less re-rendering. 3. Some updates are inherently less important than others. For example, if you're writing a live-updating search filter input like [this](https://zeit.co/blog/domains-search-web#asynchronous-rendering), it is essential that the input is updated immediately (within a few milliseconds). Re-rendering the result list can be done later, and should not block the thread or cause stutter when typing. It would be nice if React had a way to mark the latter updates as having a lower priority. (Note that even debouncing the input doesn't help because if the rendering is synchronous—like in React today—a keystroke can't interrupt the rendering if it already started. Asynchronous rendering solves this by splitting rendering into small chunks that can be paused and later restarted.) 4. For UI elements like hidden popups and tabs, we'd like to be able to start pre-rendering their content when the browser isn't busy. This way, they can appear instantaneously in response to a later user interaction. However, we don't want to make the initial rendering slower, so it's essential to render such elements lazily ([when the browser is idle](https://developers.google.com/web/updates/2015/08/using-requestidlecallback)). diff --git a/examples/update-on-async-rendering/initializing-state-after.js b/examples/update-on-async-rendering/initializing-state-after.js index 8bea2886fe3..72fe66caef9 100644 --- a/examples/update-on-async-rendering/initializing-state-after.js +++ b/examples/update-on-async-rendering/initializing-state-after.js @@ -2,7 +2,7 @@ class ExampleComponent extends React.Component { // highlight-range{1-4} state = { - isScrollingDown: false, - lastRow: this.props.currentRow, + currentColor: this.props.defaultColor, + palette: 'rgb' }; } diff --git a/examples/update-on-async-rendering/initializing-state-before.js b/examples/update-on-async-rendering/initializing-state-before.js index 9dd5c009d0b..92e37349aa2 100644 --- a/examples/update-on-async-rendering/initializing-state-before.js +++ b/examples/update-on-async-rendering/initializing-state-before.js @@ -5,8 +5,8 @@ class ExampleComponent extends React.Component { // highlight-range{1-6} componentWillMount() { this.setState({ - isScrollingDown: false, - lastRow: this.props.currentRow, + currentColor: this.props.defaultColor, + palette: 'rgb' }); } -} +} \ No newline at end of file From 06d5be4571782c46e6b5c45498a0d90b58706183 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 7 Feb 2018 08:10:28 -0800 Subject: [PATCH 12/61] Prettier --- .../initializing-state-after.js | 7 ++++--- .../initializing-state-before.js | 9 +++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/examples/update-on-async-rendering/initializing-state-after.js b/examples/update-on-async-rendering/initializing-state-after.js index 72fe66caef9..5ccb3a6cc61 100644 --- a/examples/update-on-async-rendering/initializing-state-after.js +++ b/examples/update-on-async-rendering/initializing-state-after.js @@ -1,8 +1,9 @@ // After class ExampleComponent extends React.Component { - // highlight-range{1-4} + // highlight-range{1-5} state = { - currentColor: this.props.defaultColor, - palette: 'rgb' + currentColor: this.props + .defaultColor, + palette: 'rgb', }; } diff --git a/examples/update-on-async-rendering/initializing-state-before.js b/examples/update-on-async-rendering/initializing-state-before.js index 92e37349aa2..9f5aa715343 100644 --- a/examples/update-on-async-rendering/initializing-state-before.js +++ b/examples/update-on-async-rendering/initializing-state-before.js @@ -2,11 +2,12 @@ class ExampleComponent extends React.Component { state = {}; - // highlight-range{1-6} + // highlight-range{1-7} componentWillMount() { this.setState({ - currentColor: this.props.defaultColor, - palette: 'rgb' + currentColor: this.props + .defaultColor, + palette: 'rgb', }); } -} \ No newline at end of file +} From 36963886ebe68a43c1f4ed8a32cf1e781c566dd5 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 7 Feb 2018 08:20:34 -0800 Subject: [PATCH 13/61] Increased Prettier line-width for examples --- examples/16-3-release-blog-create-ref.js | 9 ++------- .../adding-event-listeners-after.js | 10 ++++------ .../adding-event-listeners-before.js | 5 ++--- .../fetching-external-data-after.js | 6 ++---- .../fetching-external-data-before.js | 6 ++---- .../initializing-state-after.js | 5 ++--- .../initializing-state-before.js | 5 ++--- .../invoking-external-callbacks-after.js | 12 +++--------- .../invoking-external-callbacks-before.js | 12 +++--------- .../side-effects-in-constructor.js | 4 +--- .../updating-state-from-props-after.js | 16 ++++------------ .../updating-state-from-props-before.js | 10 +++------- .../using-react-lifecycles-compat.js | 5 +---- 13 files changed, 31 insertions(+), 74 deletions(-) diff --git a/examples/16-3-release-blog-create-ref.js b/examples/16-3-release-blog-create-ref.js index f189e0d0667..77cbb40a70d 100644 --- a/examples/16-3-release-blog-create-ref.js +++ b/examples/16-3-release-blog-create-ref.js @@ -3,13 +3,8 @@ class MyComponent extends React.Component { divRef = React.createRef(); render() { - // highlight-range{4} - return ( - - ); + // highlight-next-line + return ; } componentDidMount() { diff --git a/examples/update-on-async-rendering/adding-event-listeners-after.js b/examples/update-on-async-rendering/adding-event-listeners-after.js index dbff8e0e049..c3586fcaf4d 100644 --- a/examples/update-on-async-rendering/adding-event-listeners-after.js +++ b/examples/update-on-async-rendering/adding-event-listeners-after.js @@ -1,12 +1,11 @@ // After class ExampleComponent extends React.Component { - // highlight-range{1-4} + // highlight-range{1-3} state = { - subscribedValue: this.props - .dataSource.value, + subscribedValue: this.props.dataSource.value, }; - // highlight-range{1-19} + // highlight-range{1-18} componentDidMount() { // Event listeners are only safe to add after mount, // So they won't leak if mount is interrupted or errors. @@ -21,8 +20,7 @@ class ExampleComponent extends React.Component { this.props.dataSource.value ) { this.setState({ - subscribedValue: this.props - .dataSource.value, + subscribedValue: this.props.dataSource.value, }); } } diff --git a/examples/update-on-async-rendering/adding-event-listeners-before.js b/examples/update-on-async-rendering/adding-event-listeners-before.js index 9f931de22ed..9c383d78a3c 100644 --- a/examples/update-on-async-rendering/adding-event-listeners-before.js +++ b/examples/update-on-async-rendering/adding-event-listeners-before.js @@ -1,10 +1,9 @@ // Before class ExampleComponent extends React.Component { - // highlight-range{1-11} + // highlight-range{1-10} componentWillMount() { this.setState({ - subscribedValue: this.props - .dataSource.value, + subscribedValue: this.props.dataSource.value, }); // This is not safe; it can leak! diff --git a/examples/update-on-async-rendering/fetching-external-data-after.js b/examples/update-on-async-rendering/fetching-external-data-after.js index dc237168e9b..efbdf67174d 100644 --- a/examples/update-on-async-rendering/fetching-external-data-after.js +++ b/examples/update-on-async-rendering/fetching-external-data-after.js @@ -7,11 +7,9 @@ class ExampleComponent extends React.Component { externalData: null, }; - // highlight-range{1-9} + // highlight-range{1-7} componentDidMount() { - asyncLoadData( - this.props.someId - ).then(externalData => { + asyncLoadData(this.props.someId).then(externalData => { if (!this._hasUnmounted) { this.setState({externalData}); } diff --git a/examples/update-on-async-rendering/fetching-external-data-before.js b/examples/update-on-async-rendering/fetching-external-data-before.js index bd8c430a624..c7d4e02582b 100644 --- a/examples/update-on-async-rendering/fetching-external-data-before.js +++ b/examples/update-on-async-rendering/fetching-external-data-before.js @@ -4,11 +4,9 @@ class ExampleComponent extends React.Component { externalData: null, }; - // highlight-range{1-7} + // highlight-range{1-5} componentWillMount() { - asyncLoadData( - this.props.someId - ).then(externalData => + asyncLoadData(this.props.someId).then(externalData => this.setState({externalData}) ); } diff --git a/examples/update-on-async-rendering/initializing-state-after.js b/examples/update-on-async-rendering/initializing-state-after.js index 5ccb3a6cc61..077afc88556 100644 --- a/examples/update-on-async-rendering/initializing-state-after.js +++ b/examples/update-on-async-rendering/initializing-state-after.js @@ -1,9 +1,8 @@ // After class ExampleComponent extends React.Component { - // highlight-range{1-5} + // highlight-range{1-4} state = { - currentColor: this.props - .defaultColor, + currentColor: this.props.defaultColor, palette: 'rgb', }; } diff --git a/examples/update-on-async-rendering/initializing-state-before.js b/examples/update-on-async-rendering/initializing-state-before.js index 9f5aa715343..5486bf84942 100644 --- a/examples/update-on-async-rendering/initializing-state-before.js +++ b/examples/update-on-async-rendering/initializing-state-before.js @@ -2,11 +2,10 @@ class ExampleComponent extends React.Component { state = {}; - // highlight-range{1-7} + // highlight-range{1-6} componentWillMount() { this.setState({ - currentColor: this.props - .defaultColor, + currentColor: this.props.defaultColor, palette: 'rgb', }); } diff --git a/examples/update-on-async-rendering/invoking-external-callbacks-after.js b/examples/update-on-async-rendering/invoking-external-callbacks-after.js index 57a3ad5de02..8fa605ffa9b 100644 --- a/examples/update-on-async-rendering/invoking-external-callbacks-after.js +++ b/examples/update-on-async-rendering/invoking-external-callbacks-after.js @@ -1,18 +1,12 @@ // After class ExampleComponent extends React.Component { - // highlight-next-line - componentDidUpdate( - prevProps, - prevState - ) { - // highlight-range{1-8} + // highlight-range{1-8} + componentDidUpdate(prevProps, prevState) { if ( this.state.someStatefulValue !== prevState.someStatefulValue ) { - this.props.onChange( - this.state.someStatefulValue - ); + this.props.onChange(this.state.someStatefulValue); } } } diff --git a/examples/update-on-async-rendering/invoking-external-callbacks-before.js b/examples/update-on-async-rendering/invoking-external-callbacks-before.js index 3fbc002c0b2..56fd7a460ae 100644 --- a/examples/update-on-async-rendering/invoking-external-callbacks-before.js +++ b/examples/update-on-async-rendering/invoking-external-callbacks-before.js @@ -1,18 +1,12 @@ // Before class ExampleComponent extends React.Component { - // highlight-next-line - componentWillUpdate( - nextProps, - nextState - ) { - // highlight-range{1-8} + // highlight-range{1-8} + componentWillUpdate(nextProps, nextState) { if ( this.state.someStatefulValue !== nextState.someStatefulValue ) { - nextProps.onChange( - nextState.someStatefulValue - ); + nextProps.onChange(nextState.someStatefulValue); } } } diff --git a/examples/update-on-async-rendering/side-effects-in-constructor.js b/examples/update-on-async-rendering/side-effects-in-constructor.js index 004887c6517..f0686a4bb50 100644 --- a/examples/update-on-async-rendering/side-effects-in-constructor.js +++ b/examples/update-on-async-rendering/side-effects-in-constructor.js @@ -2,8 +2,6 @@ class TopLevelRoute extends React.Component { constructor(props) { super(props); - SharedApplicationState.recordEvent( - 'ExampleComponent' - ); + SharedApplicationState.recordEvent('ExampleComponent'); } } diff --git a/examples/update-on-async-rendering/updating-state-from-props-after.js b/examples/update-on-async-rendering/updating-state-from-props-after.js index d155996178e..7e55ff98861 100644 --- a/examples/update-on-async-rendering/updating-state-from-props-after.js +++ b/examples/update-on-async-rendering/updating-state-from-props-after.js @@ -5,21 +5,13 @@ class ExampleComponent extends React.Component { // highlight-next-line state = {}; - // highlight-next-line - static getDerivedStateFromProps( - nextProps, - prevState - ) { - // highlight-range{1-11} - if ( - nextProps.currentRow !== - prevState.lastRow - ) { + // highlight-range{1-8} + static getDerivedStateFromProps(nextProps, prevState) { + if (nextProps.currentRow !== prevState.lastRow) { return { lastRow: nextProps.currentRow, isScrollingDown: - nextProps.currentRow > - prevState.lastRow, + nextProps.currentRow > prevState.lastRow, }; } diff --git a/examples/update-on-async-rendering/updating-state-from-props-before.js b/examples/update-on-async-rendering/updating-state-from-props-before.js index 0e75ef38318..03dc23cf908 100644 --- a/examples/update-on-async-rendering/updating-state-from-props-before.js +++ b/examples/update-on-async-rendering/updating-state-from-props-before.js @@ -4,16 +4,12 @@ class ExampleComponent extends React.Component { isScrollingDown: false, }; - // highlight-range{1-12} + // highlight-range{1-8} componentWillReceiveProps(nextProps) { - if ( - this.props.currentRow !== - nextProps.currentRow - ) { + if (this.props.currentRow !== nextProps.currentRow) { this.setState({ isScrollingDown: - nextProps.currentRow > - this.props.currentRow, + nextProps.currentRow > this.props.currentRow, }); } } diff --git a/examples/update-on-async-rendering/using-react-lifecycles-compat.js b/examples/update-on-async-rendering/using-react-lifecycles-compat.js index 0913181b5df..925f64acb18 100644 --- a/examples/update-on-async-rendering/using-react-lifecycles-compat.js +++ b/examples/update-on-async-rendering/using-react-lifecycles-compat.js @@ -4,10 +4,7 @@ import polyfill from 'react-lifecycles-compat'; class ExampleComponent extends React.Component { // highlight-next-line - static getDerivedStateFromProps( - nextProps, - prevState - ) { + static getDerivedStateFromProps(nextProps, prevState) { // Your state update logic here ... } } From ac23b1f9089d302c8f9607ed618fec3e8b0d5ab9 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 7 Feb 2018 09:28:39 -0800 Subject: [PATCH 14/61] Trigger Netlify rebuild From 5cae7c6282cd864e60d7bd0d355b106cc3fb1ed1 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 7 Feb 2018 12:22:30 -0800 Subject: [PATCH 15/61] Strict Mode blog title capitalization --- content/blog/2018-02-07-strict-mode.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2018-02-07-strict-mode.md b/content/blog/2018-02-07-strict-mode.md index 941bce27d5c..3e48f165fd4 100644 --- a/content/blog/2018-02-07-strict-mode.md +++ b/content/blog/2018-02-07-strict-mode.md @@ -1,5 +1,5 @@ --- -title: Strict mode +title: Strict Mode author: [bvaughn] --- From f70c0dd9dfbe4dd9242e6bcc60f51c2549e63b00 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 7 Feb 2018 15:03:51 -0800 Subject: [PATCH 16/61] Minor wordsmithing --- .../blog/2018-02-07-update-on-async-rendering.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index fcc73675c41..69d71fe509b 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -5,13 +5,13 @@ author: [bvaughn, gaearon] For the past few months, the React team has been experimenting with [asynchronous rendering](/blog/2017/09/26/react-v16.0.html#new-core-architecture), and we are very excited about the new features it enables. -Along the way our research has shown that some of our legacy component lifecycles tend to encourage unsafe coding practices. They are: +Along the way, our research has shown that some of our legacy component lifecycles tend to encourage unsafe coding practices. They are: * `componentWillMount` * `componentWillReceiveProps` * `componentWillUpdate` -Because of this, we are adding an "UNSAFE_" prefix to these lifecycles in a future release. React [follows semantic versioning](/blog/2016/02/19/new-versioning-scheme.html), so the migration path is gradual: +Because of this, we are adding an "UNSAFE_" prefix to these lifecycles in a future release. React [follows semantic versioning](/blog/2016/02/19/new-versioning-scheme.html), so the migration path will be gradual: * **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.) * **16.4**: Enable deprecation warning for `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate`. (Both the old lifecycle names and the new aliases will work in this release.) @@ -23,13 +23,13 @@ In this post, we will explore some of the potential capabilities of async render #### With every new version, our goal is to improve the user experience of apps created with React. -We have been fine-tuning the performance of React with every new release. However, despite what synthetic benchmarks say, we've found that the real bottleneck is generally not React itself, but the application code using it. In order to unlock the next wave of performance optimizations and new features, we need React to be smarter about when to re-render components and flush updates to the screen. +We have been fine-tuning the performance of React with every new release. However, despite what synthetic benchmarks say, we've found that the real bottleneck is generally not React itself, but the application code that uses it. In order to unlock the next wave of performance optimizations and new features, we need React to be smarter about when to re-render components and flush updates to the screen. We found that asynchronous rendering can help in several ways. For example: 1. As users navigate within an app, newly displayed components often have asynchronous dependencies (including data, images, and code splitting). This leads to a lot of boilerplate code managing data fetching and displaying the loading states. It can also lead to a "cascade of spinners" as the data loads, causing DOM reflows and janky user experience. We'd like to make it easier for product developers to express asynchronous dependencies of components. React could keep the old UI "alive" and interactive for a certain period while the updated UI is not ready yet, and provide a declarative way to show a loading indicator if it takes more than a second. 2. Fast updates within a short timeframe often cause jank because React processes each update individually. We'd like to automatically "combine" updates within a few hundred milliseconds when possible so that there is less re-rendering. -3. Some updates are inherently less important than others. For example, if you're writing a live-updating search filter input like [this](https://zeit.co/blog/domains-search-web#asynchronous-rendering), it is essential that the input is updated immediately (within a few milliseconds). Re-rendering the result list can be done later, and should not block the thread or cause stutter when typing. It would be nice if React had a way to mark the latter updates as having a lower priority. (Note that even debouncing the input doesn't help because if the rendering is synchronous—like in React today—a keystroke can't interrupt the rendering if it already started. Asynchronous rendering solves this by splitting rendering into small chunks that can be paused and later restarted.) +3. Some updates are inherently less important than others. For example, if you're writing a live-updating search filter input like [this](https://zeit.co/blog/domains-search-web#asynchronous-rendering), it is essential that the input is updated immediately (within a few milliseconds). Re-rendering the result list can be done later, and should not block the thread or cause stutter when typing. It would be nice if React had a way to mark the latter updates as having a lower priority. (Note that even debouncing the input doesn't help because if the rendering is synchronous—like in React today—a keystroke can't interrupt the rendering once it has started. Asynchronous rendering solves this by splitting rendering into small chunks that can be paused and later resumed.) 4. For UI elements like hidden popups and tabs, we'd like to be able to start pre-rendering their content when the browser isn't busy. This way, they can appear instantaneously in response to a later user interaction. However, we don't want to make the initial rendering slower, so it's essential to render such elements lazily ([when the browser is idle](https://developers.google.com/web/updates/2015/08/using-requestidlecallback)). 5. For many apps, React is not the only JavaScript on the page. It often has to coordinate with other JS libraries, server-rendered widgets, and so on. Asynchronous rendering lets React better coordinate with non-React code regarding when components are inserted into the DOM so that [the user experience is smooth](https://twitter.com/acdlite/status/909926793536094209). @@ -48,7 +48,7 @@ However, if you'd like to start using the new component API (or if you're a main This example shows a component with `setState` calls inside of `componentWillMount`: `embed:update-on-async-rendering/initializing-state-before.js` -The simplest refactor for this type of component is to move the state-updates to the constructor or to a property initializer, like so: +The simplest refactor for this type of component is to move state initialization to the constructor or to a property initializer, like so: `embed:update-on-async-rendering/initializing-state-after.js` ### Fetching external data @@ -72,7 +72,7 @@ Here is an example of a component that subscribes to an external event dispatche 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 paired, but that is not guaranteed. Only once `componentDidMount` has been called does React guarantee that `componentWillUnmount` will later be called for clean up. +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: `embed:update-on-async-rendering/adding-event-listeners-after.js` @@ -96,7 +96,7 @@ As of version 16.3, the recommended way to update `state` in response to `props` Here is an example of a component that calls an external function when its internal state changes: `embed:update-on-async-rendering/invoking-external-callbacks-before.js` -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 unavoidable (for example, if you need to position a tooltip after measuring the rendered DOM element). +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: `embed:update-on-async-rendering/invoking-external-callbacks-after.js` From c1e67bec90739f2dea51af13863932a0898540f9 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 12 Feb 2018 09:06:10 -0800 Subject: [PATCH 17/61] Wordsmithing in response to PR feedback --- content/blog/2018-02-07-update-on-async-rendering.md | 4 +++- .../adding-event-listeners-after.js | 6 +++--- .../adding-event-listeners-before.js | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index 69d71fe509b..7f50ce37da7 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -39,7 +39,7 @@ In the next section, we'll look at how to update your existing components to pre ## Updating class components -#### If you're an application developer, **you don't have to do anything about the deprecated methods yet**. The primary purpose of this update (v16.3) is to enable open source project maintainers to update their libraries in advance of any deprecation warnings. Those warnings will be enabled with the next minor release, v16.4. +#### If you're an application developer, **you don't have to do anything about the legacy methods yet**. The primary purpose of this update (v16.3) is to enable open source project maintainers to update their libraries in advance of any deprecation warnings. Those warnings will be enabled with the next minor release, v16.4. However, if you'd like to start using the new component API (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's async-safe. @@ -61,6 +61,8 @@ The above code is problematic for both server rendering (where the external data The upgrade path for this is to move data-fetching into `componentDidMount`: `embed:update-on-async-rendering/fetching-external-data-after.js` +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](https://gist.github.com/bvaughn/89700e525ff423a75ffb63b1b1e30a8f). diff --git a/examples/update-on-async-rendering/adding-event-listeners-after.js b/examples/update-on-async-rendering/adding-event-listeners-after.js index c3586fcaf4d..d6d09a1f187 100644 --- a/examples/update-on-async-rendering/adding-event-listeners-after.js +++ b/examples/update-on-async-rendering/adding-event-listeners-after.js @@ -10,7 +10,7 @@ class ExampleComponent extends React.Component { // 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._onSubscriptionChange + this.handleSubscriptionChange ); // External values could change between render and mount, @@ -27,11 +27,11 @@ class ExampleComponent extends React.Component { componentWillUnmount() { this.props.dataSource.unsubscribe( - this._onSubscriptionChange + this.handleSubscriptionChange ); } - _onSubscriptionChange = subscribedValue => { + handleSubscriptionChange = subscribedValue => { this.setState({subscribedValue}); }; } diff --git a/examples/update-on-async-rendering/adding-event-listeners-before.js b/examples/update-on-async-rendering/adding-event-listeners-before.js index 9c383d78a3c..c83da8a1c76 100644 --- a/examples/update-on-async-rendering/adding-event-listeners-before.js +++ b/examples/update-on-async-rendering/adding-event-listeners-before.js @@ -8,17 +8,17 @@ class ExampleComponent extends React.Component { // This is not safe; it can leak! this.props.dataSource.subscribe( - this._onSubscriptionChange + this.handleSubscriptionChange ); } componentWillUnmount() { this.props.dataSource.unsubscribe( - this._onSubscriptionChange + this.handleSubscriptionChange ); } - _onSubscriptionChange = subscribedValue => { + handleSubscriptionChange = subscribedValue => { this.setState({subscribedValue}); }; } From 75a43aab34bbca1293df3d87cb4174e2c090c514 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 12 Feb 2018 13:07:19 -0800 Subject: [PATCH 18/61] Wordsmithing in response to PR feedback --- content/blog/2018-02-07-strict-mode.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/content/blog/2018-02-07-strict-mode.md b/content/blog/2018-02-07-strict-mode.md index 3e48f165fd4..5ce9c368326 100644 --- a/content/blog/2018-02-07-strict-mode.md +++ b/content/blog/2018-02-07-strict-mode.md @@ -1,5 +1,5 @@ --- -title: Strict Mode +title: Introducing Strict Mode author: [bvaughn] --- @@ -23,7 +23,7 @@ Additional functionality will be added with future releases of React. ### Identifying unsafe lifecycles -As previously mentioned, certain legacy lifecycle methods are unsafe for use in async React applications. However, if your application uses third party libraries, it can be difficult to ensure that these lifecycles aren't being used. Fortunately, strict mode can help with this! +As explained [in this blog post](/blog/2018/02/07/update-on-async-rendering.html), certain legacy lifecycle methods are unsafe for use in async React applications. However, if your application uses third party libraries, it can be difficult to ensure that these lifecycles aren't being used. Fortunately, strict mode can help with this! When strict mode is enabled, React compiles a list of all class components using the unsafe lifecycles, and logs a warning message with information about these components, like so: @@ -44,6 +44,8 @@ Version 16.3 adds a new option for managing refs that offers the convenience of > > You don't need to replace callback refs in your components. They are slightly more flexible, so they will remain as an advanced feature. +Since object refs were largely added as a replacement for string refs, strict mode now warns about usage of string refs. + [Learn more about the new `createRef` API here.](#) ### Detecting unexpected side effects @@ -64,6 +66,6 @@ Strict mode can't automatically detect side effects for you, but it can help you For example, consider the following code: `embed:update-on-async-rendering/side-effects-in-constructor.js` -At first glance, this code might not seem problematic. But if `SharedApplicationState.recordEvent` is not idempotent, then instantiating this component multiple times could lead to invalid application state. This sort of subtle bug might not manifest during development, or it might do so inconsistently and so be overlooked. +At first glance, this code might not seem problematic. But if `SharedApplicationState.recordEvent` is not [idempotent](https://en.wikipedia.org/wiki/Idempotence#Computer_science_meaning), then instantiating this component multiple times could lead to invalid application state. This sort of subtle bug might not manifest during development, or it might do so inconsistently and so be overlooked. By intentionally double-invoking methods like the component constructor, strict mode makes patterns like this easier to spot. From fb3b91f06d2e75c11b903898305cac804fff72d0 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 12 Feb 2018 14:59:17 -0800 Subject: [PATCH 19/61] Add explanation for render and commit lifecycles --- content/blog/2018-02-07-strict-mode.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/content/blog/2018-02-07-strict-mode.md b/content/blog/2018-02-07-strict-mode.md index 5ce9c368326..b5eb18900ef 100644 --- a/content/blog/2018-02-07-strict-mode.md +++ b/content/blog/2018-02-07-strict-mode.md @@ -50,7 +50,23 @@ Since object refs were largely added as a replacement for string refs, strict mo ### Detecting unexpected side effects -As a general rule, side-effects should be avoided in certain class component methods (e.g. the `constructor`, `render`, etc). This is because React may invoke these methods more than once before committing, or it may invoke them without committing at all (because of an error or a higher priority interruption). Ignoring this rule can lead to a variety of problems, including memory leaks and invalid state. Unfortunately, it can be difficult to detect these problems as they are often non-deterministic. +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 lifecycles like `getDerivedStateFromProps` and `render` and then compares the result to the previous render. +* The **commit** phase is when React applies any changes. (In the case of ReactDOM, this is when React inserts, updates, and removes DOM nodes.) React calls lifecycles like `componentDidMount` and `componentDidUpdate` during this phase. + +The commit phase is very fast, but rendering can be slow. For this reason, async mode breaks rendering into multiple pieces, pausing and resuming the work to avoid blocking the browser. This means that React may invoke render phase lifecycles more than once before committing, or it may invoke them without committing at all (because of an error or a higher priority interruption). + +Render phase lifecycles include the following class component methods: +* `constructor` +* `componentWillMount` +* `componentWillReceiveProps` +* `componentWillUpdate` +* `getDerivedStateFromProps` +* `shouldComponentUpdate` +* `render` +* `setState` updater functions + +Because the above methods might be called more than once, it's important that they do not contain side-effects. Ignoring this rule can lead to a variety of problems, including memory leaks and invalid application state. Unfortunately, it can be difficult to detect these problems as they are often [non-deterministic](https://en.wikipedia.org/wiki/Deterministic_algorithm). Strict mode can't automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following methods: From 60d65ce23a303093532f6543213c97db691d1adc Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 12 Feb 2018 15:26:25 -0800 Subject: [PATCH 20/61] Wordsmithing --- content/blog/2018-02-07-strict-mode.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/content/blog/2018-02-07-strict-mode.md b/content/blog/2018-02-07-strict-mode.md index b5eb18900ef..81bdda0fb79 100644 --- a/content/blog/2018-02-07-strict-mode.md +++ b/content/blog/2018-02-07-strict-mode.md @@ -51,10 +51,10 @@ Since object refs were largely added as a replacement for string refs, strict mo ### Detecting unexpected side effects 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 lifecycles like `getDerivedStateFromProps` and `render` and then compares the result to the previous render. -* The **commit** phase is when React applies any changes. (In the case of ReactDOM, this is when React inserts, updates, and removes DOM nodes.) React calls lifecycles like `componentDidMount` and `componentDidUpdate` during this phase. +* 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 ReactDOM, this is when React inserts, updates, and removes DOM nodes.) React also calls lifecycles like `componentDidMount` and `componentDidUpdate` during this phase. -The commit phase is very fast, but rendering can be slow. For this reason, async mode breaks rendering into multiple pieces, pausing and resuming the work to avoid blocking the browser. This means that React may invoke render phase lifecycles more than once before committing, or it may invoke them without committing at all (because of an error or a higher priority interruption). +The commit phase is usually very fast, but rendering can be slow. For this reason, the upcoming async mode (which is not enabled by default yet) breaks rendering into multiple pieces, pausing and resuming the work to avoid blocking the browser. This means that React may invoke render phase lifecycles more than once before committing, or it may invoke them without committing at all (because of an error or a higher priority interruption). Render phase lifecycles include the following class component methods: * `constructor` @@ -64,7 +64,7 @@ Render phase lifecycles include the following class component methods: * `getDerivedStateFromProps` * `shouldComponentUpdate` * `render` -* `setState` updater functions +* `setState` updater functions (the first argument) Because the above methods might be called more than once, it's important that they do not contain side-effects. Ignoring this rule can lead to a variety of problems, including memory leaks and invalid application state. Unfortunately, it can be difficult to detect these problems as they are often [non-deterministic](https://en.wikipedia.org/wiki/Deterministic_algorithm). @@ -72,7 +72,7 @@ Strict mode can't automatically detect side effects for you, but it can help you * Class component `constructor` method * The `render` method -* `setState` updater functions +* `setState` updater functions (the first argument) * The static `getDerivedStateFromProps` lifecycle > Note: From f632f220c6045e03c3d85afc23f52102758a1f9b Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 13 Feb 2018 10:49:40 -0800 Subject: [PATCH 21/61] Wording changes for update-on-async and strict-mode Incorporates most of Sophie's feedback. Changes update-on-async references to 16.3 to be future tense (since this blog post will be released before 16.3). Changes strict-mode references to be past-tense (since this will be a docs page instead of a blog post). --- content/blog/2018-02-07-strict-mode.md | 20 ++++---- .../2018-02-07-update-on-async-rendering.md | 50 ++++++++++++++----- .../adding-event-listeners-after.js | 2 +- .../enabling-strict-mode.js | 15 +++--- .../fetching-external-data-after.js | 21 ++++---- .../updating-state-from-props-after.js | 8 +-- 6 files changed, 70 insertions(+), 46 deletions(-) diff --git a/content/blog/2018-02-07-strict-mode.md b/content/blog/2018-02-07-strict-mode.md index 81bdda0fb79..1ed636e47c3 100644 --- a/content/blog/2018-02-07-strict-mode.md +++ b/content/blog/2018-02-07-strict-mode.md @@ -1,9 +1,9 @@ --- -title: Introducing Strict Mode +title: StrictMode author: [bvaughn] --- -`StrictMode` is a tool for highlighting potential problems in an application. Like `Fragment`, `StrictMode` does not render any visible UI. It simply activates additional checks and warnings for its descendants. +`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: > @@ -12,9 +12,9 @@ author: [bvaughn] You can enable strict mode for any part of your application. For example: `embed:update-on-async-rendering/enabling-strict-mode.js` -In the above example, strict mode checks will *not* be run against the `Header` and `Footer` components. However, `RouteOne` and `RouteTwo`, as well as all of their descendants, will have the checks. +In the above example, strict mode checks will *not* be run against the `Header` and `Footer` components. However, `ComponentOne` and `ComponentTwo`, as well as all of their descendants, will have the checks. -In version 16.3, `StrictMode` helps with: +`StrictMode` currently helps with: * [Identifying components with unsafe lifecycles](#identifying-unsafe-lifecycles) * [Warning about legacy string ref API usage](#warning-about-legacy-string-ref-api-usage) * [Detecting unexpected side effects](#detecting-unexpected-side-effects) @@ -35,24 +35,24 @@ Addressing the issues identified by strict mode _now_ will make it easier for yo Previously, React provided two ways for 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](https://github.com/facebook/react/issues/1373) and so our official recomendation was to [use the callback form instead](https://reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs). -Version 16.3 adds a new option for managing refs that offers the convenience of a string ref without any of the downsides: +React 16.3 added a third option that offers the convenience of a string ref without any of the downsides: `embed:16-3-release-blog-create-ref.js` +Since object refs were largely added as a replacement for string refs, strict mode now warns about usage of string refs. + > **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. -Since object refs were largely added as a replacement for string refs, strict mode now warns about usage of string refs. - -[Learn more about the new `createRef` API here.](#) +[Learn more about the new `createRef` API here.](/docs/refs-and-the-dom.html) ### Detecting unexpected side effects 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 ReactDOM, this is when React inserts, updates, and removes DOM nodes.) React also calls lifecycles like `componentDidMount` and `componentDidUpdate` during this phase. +* 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 commit phase is usually very fast, but rendering can be slow. For this reason, the upcoming async mode (which is not enabled by default yet) breaks rendering into multiple pieces, pausing and resuming the work to avoid blocking the browser. This means that React may invoke render phase lifecycles more than once before committing, or it may invoke them without committing at all (because of an error or a higher priority interruption). @@ -66,7 +66,7 @@ Render phase lifecycles include the following class component methods: * `render` * `setState` updater functions (the first argument) -Because the above methods might be called more than once, it's important that they do not contain side-effects. Ignoring this rule can lead to a variety of problems, including memory leaks and invalid application state. Unfortunately, it can be difficult to detect these problems as they are often [non-deterministic](https://en.wikipedia.org/wiki/Deterministic_algorithm). +Because the above methods might be called more than once, it's important that they do not contain side-effects. Ignoring this rule can lead to a variety of problems, including memory leaks and invalid application state. Unfortunately, it can be difficult to detect these problems as they can often be [non-deterministic](https://en.wikipedia.org/wiki/Deterministic_algorithm). Strict mode can't automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following methods: diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index 7f50ce37da7..33e8c89a9ca 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -5,13 +5,13 @@ author: [bvaughn, gaearon] For the past few months, the React team has been experimenting with [asynchronous rendering](/blog/2017/09/26/react-v16.0.html#new-core-architecture), and we are very excited about the new features it enables. -Along the way, our research has shown that some of our legacy component lifecycles tend to encourage unsafe coding practices. They are: +Along the way, our research has shown that some of our component lifecycles tend to encourage unsafe coding practices. They are: * `componentWillMount` * `componentWillReceiveProps` * `componentWillUpdate` -Because of this, we are adding an "UNSAFE_" prefix to these lifecycles in a future release. React [follows semantic versioning](/blog/2016/02/19/new-versioning-scheme.html), so the migration path will be gradual: +These lifecycle methods have often been misunderstood and subtly misused; furthermore, we anticipate that these potential problems may be more prominent with async rendering. Because of this, we are adding an "UNSAFE_" prefix to these lifecycles in a future release. React [follows semantic versioning](/blog/2016/02/19/new-versioning-scheme.html), so the migration path will be gradual: * **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.) * **16.4**: Enable deprecation warning for `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate`. (Both the old lifecycle names and the new aliases will work in this release.) @@ -21,17 +21,16 @@ In this post, we will explore some of the potential capabilities of async render ## What can asynchronous rendering do? -#### With every new version, our goal is to improve the user experience of apps created with React. +#### With every new version, our goal is to make it easier for developers using React to build great user experiences -We have been fine-tuning the performance of React with every new release. However, despite what synthetic benchmarks say, we've found that the real bottleneck is generally not React itself, but the application code that uses it. In order to unlock the next wave of performance optimizations and new features, we need React to be smarter about when to re-render components and flush updates to the screen. +We have been fine-tuning the performance of React with every new release. However, despite what synthetic benchmarks say, we've found that the bottleneck in real-world apps is generally not React itself, but the application code that uses it. In order to unlock the next wave of performance optimizations and new features, we need React to be smarter about when to re-render components and flush updates to the screen. We found that asynchronous rendering can help in several ways. For example: -1. As users navigate within an app, newly displayed components often have asynchronous dependencies (including data, images, and code splitting). This leads to a lot of boilerplate code managing data fetching and displaying the loading states. It can also lead to a "cascade of spinners" as the data loads, causing DOM reflows and janky user experience. We'd like to make it easier for product developers to express asynchronous dependencies of components. React could keep the old UI "alive" and interactive for a certain period while the updated UI is not ready yet, and provide a declarative way to show a loading indicator if it takes more than a second. +1. As users navigate within an app, newly displayed components often have asynchronous dependencies (including data, images, and code splitting). This leads to a lot of boilerplate code managing data fetching and displaying the loading states. It can also lead to a "cascade of spinners" as the data loads, causing DOM reflows and janky user experience. We'd like to make it easier for product developers to express asynchronous dependencies and to wait to show a component until all of its data has been loaded. React could keep the old UI "alive" and interactive for a certain period while the updated UI is not ready yet, and provide a declarative way to show a loading indicator if it takes more than a second. 2. Fast updates within a short timeframe often cause jank because React processes each update individually. We'd like to automatically "combine" updates within a few hundred milliseconds when possible so that there is less re-rendering. -3. Some updates are inherently less important than others. For example, if you're writing a live-updating search filter input like [this](https://zeit.co/blog/domains-search-web#asynchronous-rendering), it is essential that the input is updated immediately (within a few milliseconds). Re-rendering the result list can be done later, and should not block the thread or cause stutter when typing. It would be nice if React had a way to mark the latter updates as having a lower priority. (Note that even debouncing the input doesn't help because if the rendering is synchronous—like in React today—a keystroke can't interrupt the rendering once it has started. Asynchronous rendering solves this by splitting rendering into small chunks that can be paused and later resumed.) -4. For UI elements like hidden popups and tabs, we'd like to be able to start pre-rendering their content when the browser isn't busy. This way, they can appear instantaneously in response to a later user interaction. However, we don't want to make the initial rendering slower, so it's essential to render such elements lazily ([when the browser is idle](https://developers.google.com/web/updates/2015/08/using-requestidlecallback)). -5. For many apps, React is not the only JavaScript on the page. It often has to coordinate with other JS libraries, server-rendered widgets, and so on. Asynchronous rendering lets React better coordinate with non-React code regarding when components are inserted into the DOM so that [the user experience is smooth](https://twitter.com/acdlite/status/909926793536094209). +3. Some updates are inherently less important than others. For example, if you're writing a [live-updating search filter input](https://zeit.co/blog/domains-search-web#asynchronous-rendering), it is essential that the input is updated immediately (within a few milliseconds). Re-rendering the result list can be done later, and should not block the thread or cause stutter when typing. It would be nice if React had a way to mark the latter updates as having a lower priority. (Note that even debouncing the input doesn't help because if the rendering is synchronous (like in React today)—a keystroke can't interrupt the rendering once it has started. Asynchronous rendering solves this by splitting rendering into small chunks that can be paused and later resumed.) +4. For UI elements like hidden popups and tabs, we'd like to be able to start pre-rendering their content when the browser isn't busy. This way, they can appear instantaneously in response to a later user interaction. However, we'd like to do this only [when the browser is idle](https://developers.google.com/web/updates/2015/08/using-requestidlecallback) to avoid slowing down other parts of the page. Of course, it's possible to implement some of these features today, but it's difficult. We hope to make them effortless by building them into React itself. By replacing problematic lifecycles with safer alternatives, we also hope to make it simple to write async-safe React components. @@ -39,9 +38,28 @@ In the next section, we'll look at how to update your existing components to pre ## Updating class components -#### If you're an application developer, **you don't have to do anything about the legacy methods yet**. The primary purpose of this update (v16.3) is to enable open source project maintainers to update their libraries in advance of any deprecation warnings. Those warnings will be enabled with the next minor release, v16.4. +#### If you're an application developer, **you don't have to do anything about the legacy methods yet**. The primary purpose of the upcoming version 16.3 is to enable open source project maintainers to update their libraries in advance of any deprecation warnings. Those warnings will be enabled with the next minor release, version 16.4. -However, if you'd like to start using the new component API (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's async-safe. +However, if you'd like to start using the new component API (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 reminder of the lifecyle changes in 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 introducinc a new, static lifecycle, `getDerivedStateFromProps`: + +```js +static getDerivedStateFromProps( + nextProps: Props, + prevState: State +): $Shape | null +``` + +This new lifecycle is invoked after a component is instantiated and when it receives new props. It should return an object to update `state`, or `null` to indicate that the new `props` do not require any `state` updates. + +--- + +Now let's take a look at some examples. ### Initializing state @@ -58,7 +76,7 @@ Here is an example of a component that uses `componentWillMount` to fetch extern 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, or executed unnecessarily). -The upgrade path for this is to move data-fetching into `componentDidMount`: +The recommended upgrade path for most use cases is to move data-fetching into `componentDidMount`: `embed:update-on-async-rendering/fetching-external-data-after.js` 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. @@ -79,6 +97,10 @@ People often assume that `componentWillMount` and `componentWillUnmount` are alw For this reason, the recommended way to add listeners/subscriptions is to use the `componentDidMount` lifecycle: `embed:update-on-async-rendering/adding-event-listeners-after.js` +> **Note**: +> +> Although the pattern above is slightly more verbose, it has an added benefit of deferring the subscription creation until after the component has rendered, reducing the amount of time in the critical render path. In the near future, React may include more tools to reduce code complexity for data fetching cases like this. + ### 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: @@ -86,12 +108,14 @@ Here is an example of a component that uses the legacy `componentWillReceiveProp 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 has been deprecated. -As of version 16.3, the recommended way to update `state` in response to `props` changes is using the new `static getDerivedStateFromProps` lifecycle: +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.): `embed:update-on-async-rendering/updating-state-from-props-after.js` > Note: > > The [`react-lifecycles-compat`](https://github.com/reactjs/react-lifecycles-compat) polyfill allows this new lifecycle to be used with older versions of React as well. This can be helpful if you're writing a shared component that is intended for use with multiple versions of React. +> +> This polyfill is not needed for application code that uses a specific version of React. It is only useful for shared components (e.g. open source, NPM packages). ### Invoking external callbacks @@ -113,7 +137,7 @@ Open source maintainers might be wondering what these changes mean for shared co Fortunately, you do not! -Along with the release of 16.3, we've also released a new NPM package, [`react-lifecycles-compat`](https://github.com/reactjs/react-lifecycles-compat). This package polyfills components so that the new `getDerivedStateFromProps` lifecycle will also work with older versions of React (0.14.9+). +In support of version 16.3, we've also created a new NPM package, [`react-lifecycles-compat`](https://github.com/reactjs/react-lifecycles-compat). This package polyfills components so that the new `getDerivedStateFromProps` lifecycle will also work with older versions of React (0.14.9+). To use this polyfill, first add it as a dependency to your library: diff --git a/examples/update-on-async-rendering/adding-event-listeners-after.js b/examples/update-on-async-rendering/adding-event-listeners-after.js index d6d09a1f187..e6018da5e4d 100644 --- a/examples/update-on-async-rendering/adding-event-listeners-after.js +++ b/examples/update-on-async-rendering/adding-event-listeners-after.js @@ -4,7 +4,7 @@ class ExampleComponent extends React.Component { state = { subscribedValue: this.props.dataSource.value, }; - + // highlight-line // highlight-range{1-18} componentDidMount() { // Event listeners are only safe to add after mount, diff --git a/examples/update-on-async-rendering/enabling-strict-mode.js b/examples/update-on-async-rendering/enabling-strict-mode.js index 8f12aac5715..7ff62591960 100644 --- a/examples/update-on-async-rendering/enabling-strict-mode.js +++ b/examples/update-on-async-rendering/enabling-strict-mode.js @@ -1,20 +1,17 @@ import React from 'react'; -// highlight-next-line -const {StrictMode} = React; - function ExampleApplication() { return (
{/* highlight-next-line */} - - <> - - - + +
+ + +
{/* highlight-next-line */} -
+
); diff --git a/examples/update-on-async-rendering/fetching-external-data-after.js b/examples/update-on-async-rendering/fetching-external-data-after.js index efbdf67174d..4460c2158e4 100644 --- a/examples/update-on-async-rendering/fetching-external-data-after.js +++ b/examples/update-on-async-rendering/fetching-external-data-after.js @@ -1,24 +1,25 @@ // After class ExampleComponent extends React.Component { - // highlight-next-line - _hasUnmounted = false; - state = { externalData: null, }; - // highlight-range{1-7} + // highlight-range{1-9} componentDidMount() { - asyncLoadData(this.props.someId).then(externalData => { - if (!this._hasUnmounted) { + this._currentRequest = asyncLoadData( + this.props.someID, + externalData => { + this._currentRequest = null; this.setState({externalData}); } - }); + ); } - - // highlight-range{1-3} + // highlight-line + // highlight-range{1-5} componentWillUnmount() { - this._hasUnmounted = true; + if (this._currentRequest) { + this._currentRequest.cancel(); + } } render() { diff --git a/examples/update-on-async-rendering/updating-state-from-props-after.js b/examples/update-on-async-rendering/updating-state-from-props-after.js index 7e55ff98861..dc5eccded22 100644 --- a/examples/update-on-async-rendering/updating-state-from-props-after.js +++ b/examples/update-on-async-rendering/updating-state-from-props-after.js @@ -2,9 +2,11 @@ class ExampleComponent extends React.Component { // Initialize state in constructor, // Or with a property initializer. - // highlight-next-line - state = {}; - + // highlight-range{1-3} + state = { + isScrollingDown: false, + }; + // highlight-line // highlight-range{1-8} static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.currentRow !== prevState.lastRow) { From 626ac42195486516444ca4b91d4a02842e3ffb9b Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 13 Feb 2018 10:52:00 -0800 Subject: [PATCH 22/61] Moved StrictMode to docs rather than blog post --- content/docs/nav.yml | 2 ++ .../{blog/2018-02-07-strict-mode.md => docs/strict-mode.md} | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) rename content/{blog/2018-02-07-strict-mode.md => docs/strict-mode.md} (99%) diff --git a/content/docs/nav.yml b/content/docs/nav.yml index 8bbadf66a84..cb6fbdb417e 100644 --- a/content/docs/nav.yml +++ b/content/docs/nav.yml @@ -74,6 +74,8 @@ title: Accessibility - id: code-splitting title: Code-Splitting + - id: strict-mode + title: StrictMode - title: Reference items: - id: react-api diff --git a/content/blog/2018-02-07-strict-mode.md b/content/docs/strict-mode.md similarity index 99% rename from content/blog/2018-02-07-strict-mode.md rename to content/docs/strict-mode.md index 1ed636e47c3..88d2950c8c1 100644 --- a/content/blog/2018-02-07-strict-mode.md +++ b/content/docs/strict-mode.md @@ -1,6 +1,7 @@ --- +id: strict-mode title: StrictMode -author: [bvaughn] +permalink: docs/strict-mode.html --- `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. From 745632708d983c878d9e44ae0d39809a661c8bb2 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 13 Feb 2018 12:48:18 -0800 Subject: [PATCH 23/61] Combined notes about react-lifecycles-compat --- content/blog/2018-02-07-update-on-async-rendering.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index 33e8c89a9ca..f48ecd5c176 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -113,9 +113,7 @@ As of version 16.3, the recommended way to update `state` in response to `props` > Note: > -> The [`react-lifecycles-compat`](https://github.com/reactjs/react-lifecycles-compat) polyfill allows this new lifecycle to be used with older versions of React as well. This can be helpful if you're writing a shared component that is intended for use with multiple versions of React. -> -> This polyfill is not needed for application code that uses a specific version of React. It is only useful for shared components (e.g. open source, NPM packages). +> The [`react-lifecycles-compat`](https://github.com/reactjs/react-lifecycles-compat) polyfill enables this new lifecycle to be used with older versions of React as well. [Learn more about how to use it below.](http://localhost:8000/blog/2018/02/07/update-on-async-rendering.html#open-source-project-maintainers) ### Invoking external callbacks From 290973818225819270689495069e57bc5085992b Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 13 Feb 2018 13:04:59 -0800 Subject: [PATCH 24/61] Address more Sophie feedback --- content/blog/2018-02-07-update-on-async-rendering.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index f48ecd5c176..6f2fea6738d 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -25,6 +25,10 @@ In this post, we will explore some of the potential capabilities of async render We have been fine-tuning the performance of React with every new release. However, despite what synthetic benchmarks say, we've found that the bottleneck in real-world apps is generally not React itself, but the application code that uses it. In order to unlock the next wave of performance optimizations and new features, we need React to be smarter about when to re-render components and flush updates to the screen. +> **Note** +> +> React already has some optimizations in this regard. For example, React batches state updates so that if you call `setState` multiple times in quick succession, it only renders once. + We found that asynchronous rendering can help in several ways. For example: 1. As users navigate within an app, newly displayed components often have asynchronous dependencies (including data, images, and code splitting). This leads to a lot of boilerplate code managing data fetching and displaying the loading states. It can also lead to a "cascade of spinners" as the data loads, causing DOM reflows and janky user experience. We'd like to make it easier for product developers to express asynchronous dependencies and to wait to show a component until all of its data has been loaded. React could keep the old UI "alive" and interactive for a certain period while the updated UI is not ready yet, and provide a declarative way to show a loading indicator if it takes more than a second. @@ -74,7 +78,7 @@ The simplest refactor for this type of component is to move state initialization Here is an example of a component that uses `componentWillMount` to fetch external data:: `embed:update-on-async-rendering/fetching-external-data-before.js` -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, or executed unnecessarily). +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`: `embed:update-on-async-rendering/fetching-external-data-after.js` From 540033808f656c8ae06278aa4429257321859333 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 13 Feb 2018 13:48:27 -0800 Subject: [PATCH 25/61] Updated data-fetching example to show cWRP too --- .../blog/2018-02-07-update-on-async-rendering.md | 4 ++-- .../fetching-external-data-after.js | 15 ++++++++++++++- .../fetching-external-data-before.js | 11 ++++++++++- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index 6f2fea6738d..96845367a3d 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -75,12 +75,12 @@ The simplest refactor for this type of component is to move state initialization ### Fetching external data -Here is an example of a component that uses `componentWillMount` to fetch external data:: +Here is an example of a component that uses `componentWillMount` and `componentWillUpdate` to fetch external data:: `embed:update-on-async-rendering/fetching-external-data-before.js` 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`: +The recommended upgrade path for most use cases is to move data-fetching into `componentDidMount` and `componentDidUpdate`: `embed:update-on-async-rendering/fetching-external-data-after.js` 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. diff --git a/examples/update-on-async-rendering/fetching-external-data-after.js b/examples/update-on-async-rendering/fetching-external-data-after.js index 4460c2158e4..4aae7722cab 100644 --- a/examples/update-on-async-rendering/fetching-external-data-after.js +++ b/examples/update-on-async-rendering/fetching-external-data-after.js @@ -7,7 +7,7 @@ class ExampleComponent extends React.Component { // highlight-range{1-9} componentDidMount() { this._currentRequest = asyncLoadData( - this.props.someID, + this.props.id, externalData => { this._currentRequest = null; this.setState({externalData}); @@ -15,6 +15,19 @@ class ExampleComponent extends React.Component { ); } // highlight-line + // highlight-range{1-11} + componentDidUpdate(prevProps, prevState) { + if (prevProps.id !== this.props.id) { + this._currentRequest = asyncLoadData( + this.props.id, + externalData => { + this._currentRequest = null; + this.setState({externalData}); + } + ); + } + } + // highlight-line // highlight-range{1-5} componentWillUnmount() { if (this._currentRequest) { diff --git a/examples/update-on-async-rendering/fetching-external-data-before.js b/examples/update-on-async-rendering/fetching-external-data-before.js index c7d4e02582b..ebe7c4b8a55 100644 --- a/examples/update-on-async-rendering/fetching-external-data-before.js +++ b/examples/update-on-async-rendering/fetching-external-data-before.js @@ -6,10 +6,19 @@ class ExampleComponent extends React.Component { // highlight-range{1-5} componentWillMount() { - asyncLoadData(this.props.someId).then(externalData => + asyncLoadData(this.props.id).then(externalData => this.setState({externalData}) ); } + // highlight-line + // highlight-range{1-7} + componentWillReceiveProps(nextProps) { + if (nextProps.id !== this.props.id) { + asyncLoadData(this.props.id).then(externalData => + this.setState({externalData}) + ); + } + } render() { if (this.externalData === null) { From 813be17f1e0e2d1ce4a035a1330a72b534b8bf97 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 14 Feb 2018 14:34:14 -0800 Subject: [PATCH 26/61] Updated docs/recipes in response to a new GH question This question showed there was a use-case that I wasn't covering fully enough before, so I've added a new section. --- .../2018-02-07-update-on-async-rendering.md | 16 +++++- content/docs/strict-mode.md | 2 +- .../fetching-external-data-after.js | 29 +++------- .../fetching-external-data-before.js | 22 ++++---- ...g-external-data-when-props-change-after.js | 55 +++++++++++++++++++ ...-external-data-when-props-change-before.js | 41 ++++++++++++++ 6 files changed, 129 insertions(+), 36 deletions(-) create mode 100644 examples/update-on-async-rendering/updating-external-data-when-props-change-after.js create mode 100644 examples/update-on-async-rendering/updating-external-data-when-props-change-before.js diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index 96845367a3d..2eab75ffaf5 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -75,12 +75,12 @@ The simplest refactor for this type of component is to move state initialization ### Fetching external data -Here is an example of a component that uses `componentWillMount` and `componentWillUpdate` to fetch external data:: +Here is an example of a component that uses `componentWillMount` to fetch external data: `embed:update-on-async-rendering/fetching-external-data-before.js` 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` and `componentDidUpdate`: +The recommended upgrade path for most use cases is to move data-fetching into `componentDidMount`: `embed:update-on-async-rendering/fetching-external-data-after.js` 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. @@ -129,6 +129,18 @@ Sometimes people use `componentWillUpdate` out of a misplaced fear that by the t 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: `embed:update-on-async-rendering/invoking-external-callbacks-after.js` +### Updating external data when `props` change + +Here is an example of a component that fetches external data based on `props` values: +`embed:update-on-async-rendering/updating-external-data-when-props-change-before.js` + +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: +`embed:update-on-async-rendering/updating-external-data-when-props-change-after.js` + +> **Note** +> +> If you're using an HTTP library that supports cancellation, like [axios](https://www.npmjs.com/package/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](https://gist.github.com/bvaughn/982ab689a41097237f6e9860db7ca8d6). + ## 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](https://github.com/reactjs/reactjs.org/issues/new) 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. diff --git a/content/docs/strict-mode.md b/content/docs/strict-mode.md index 88d2950c8c1..1fc7944a739 100644 --- a/content/docs/strict-mode.md +++ b/content/docs/strict-mode.md @@ -55,7 +55,7 @@ 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 commit phase is usually very fast, but rendering can be slow. For this reason, the upcoming async mode (which is not enabled by default yet) breaks rendering into multiple pieces, pausing and resuming the work to avoid blocking the browser. This means that React may invoke render phase lifecycles more than once before committing, or it may invoke them without committing at all (because of an error or a higher priority interruption). +The commit phase is usually very fast, but rendering can be slow. For this reason, the upcoming async mode (which is not enabled by default yet) breaks the rendering work into pieces, pausing and resuming the work to avoid blocking the browser. This means that React may invoke render phase lifecycles more than once before committing, or it may invoke them without committing at all (because of an error or a higher priority interruption). Render phase lifecycles include the following class component methods: * `constructor` diff --git a/examples/update-on-async-rendering/fetching-external-data-after.js b/examples/update-on-async-rendering/fetching-external-data-after.js index 4aae7722cab..1e8e352d88f 100644 --- a/examples/update-on-async-rendering/fetching-external-data-after.js +++ b/examples/update-on-async-rendering/fetching-external-data-after.js @@ -4,39 +4,24 @@ class ExampleComponent extends React.Component { externalData: null, }; - // highlight-range{1-9} + // highlight-range{1-8} componentDidMount() { - this._currentRequest = asyncLoadData( - this.props.id, + this._asyncRequest = asyncLoadData().then( externalData => { - this._currentRequest = null; + this._asyncRequest = null; this.setState({externalData}); } ); } - // highlight-line - // highlight-range{1-11} - componentDidUpdate(prevProps, prevState) { - if (prevProps.id !== this.props.id) { - this._currentRequest = asyncLoadData( - this.props.id, - externalData => { - this._currentRequest = null; - this.setState({externalData}); - } - ); - } - } - // highlight-line - // highlight-range{1-5} + componentWillUnmount() { - if (this._currentRequest) { - this._currentRequest.cancel(); + if (this._asyncRequest) { + this._asyncRequest.cancel(); } } render() { - if (this.externalData === null) { + if (this.state.externalData === null) { // Render loading state ... } else { // Render real UI ... diff --git a/examples/update-on-async-rendering/fetching-external-data-before.js b/examples/update-on-async-rendering/fetching-external-data-before.js index ebe7c4b8a55..899ddcb1b74 100644 --- a/examples/update-on-async-rendering/fetching-external-data-before.js +++ b/examples/update-on-async-rendering/fetching-external-data-before.js @@ -4,24 +4,24 @@ class ExampleComponent extends React.Component { externalData: null, }; - // highlight-range{1-5} + // highlight-range{1-8} componentWillMount() { - asyncLoadData(this.props.id).then(externalData => - this.setState({externalData}) + this._asyncRequest = asyncLoadData().then( + externalData => { + this._asyncRequest = null; + this.setState({externalData}); + } ); } - // highlight-line - // highlight-range{1-7} - componentWillReceiveProps(nextProps) { - if (nextProps.id !== this.props.id) { - asyncLoadData(this.props.id).then(externalData => - this.setState({externalData}) - ); + + componentWillUnmount() { + if (this._asyncRequest) { + this._asyncRequest.cancel(); } } render() { - if (this.externalData === null) { + if (this.state.externalData === null) { // Render loading state ... } else { // Render real UI ... diff --git a/examples/update-on-async-rendering/updating-external-data-when-props-change-after.js b/examples/update-on-async-rendering/updating-external-data-when-props-change-after.js new file mode 100644 index 00000000000..014a3c6194a --- /dev/null +++ b/examples/update-on-async-rendering/updating-external-data-when-props-change-after.js @@ -0,0 +1,55 @@ +// After +class ExampleComponent extends React.Component { + state = { + externalData: null, + }; + + // highlight-range{1-13} + 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(); + } + + // highlight-range{1-5} + componentDidUpdate(prevProps, prevState) { + if (prevState.externalData === null) { + this._loadAsyncData(); + } + } + + componentWillUnmount() { + if (this._asyncRequest) { + this._asyncRequest.cancel(); + } + } + + render() { + if (this.state.externalData === null) { + // Render loading state ... + } else { + // Render real UI ... + } + } + + _loadAsyncData() { + this._asyncRequest = asyncLoadData(this.props.id).then( + externalData => { + this._asyncRequest = null; + this.setState({externalData}); + } + ); + } +} diff --git a/examples/update-on-async-rendering/updating-external-data-when-props-change-before.js b/examples/update-on-async-rendering/updating-external-data-when-props-change-before.js new file mode 100644 index 00000000000..0b0af809182 --- /dev/null +++ b/examples/update-on-async-rendering/updating-external-data-when-props-change-before.js @@ -0,0 +1,41 @@ +// Before +class ExampleComponent extends React.Component { + state = { + externalData: null, + }; + + componentDidMount() { + this._loadAsyncData(); + } + + // highlight-range{1-6} + componentWillReceiveProps(nextProps) { + if (nextProps.id !== this.props.id) { + this.setState({externalData: null}); + this._loadAsyncData(); + } + } + + componentWillUnmount() { + if (this._asyncRequest) { + this._asyncRequest.cancel(); + } + } + + render() { + if (this.state.externalData === null) { + // Render loading state ... + } else { + // Render real UI ... + } + } + + _loadAsyncData() { + this._asyncRequest = asyncLoadData(this.props.id).then( + externalData => { + this._asyncRequest = null; + this.setState({externalData}); + } + ); + } +} From 8de7dc42ba9b1964000cd5b24bd8b07b53dbb47c Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 16 Feb 2018 14:47:16 -0800 Subject: [PATCH 27/61] Updated recipes to show updating/removing subscriptions --- .../2018-02-07-update-on-async-rendering.md | 3 ++ .../adding-event-listeners-after-continued.js | 53 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 examples/update-on-async-rendering/adding-event-listeners-after-continued.js diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index 2eab75ffaf5..f7cfeba3dca 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -105,6 +105,9 @@ For this reason, the recommended way to add listeners/subscriptions is to use th > > Although the pattern above is slightly more verbose, it has an added benefit of deferring the subscription creation until after the component has rendered, reducing the amount of time in the critical render path. In the near future, React may include more tools to reduce code complexity for data fetching cases like this. +You may also need to update subscriptions based on changes in `props`. In this case, you should also wait to _remove_ a subscription until `componentDidUpdate`. For example: +`embed:update-on-async-rendering/adding-event-listeners-after-continued.js` + ### 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: diff --git a/examples/update-on-async-rendering/adding-event-listeners-after-continued.js b/examples/update-on-async-rendering/adding-event-listeners-after-continued.js new file mode 100644 index 00000000000..9ba78f20e34 --- /dev/null +++ b/examples/update-on-async-rendering/adding-event-listeners-after-continued.js @@ -0,0 +1,53 @@ +// After +class ExampleComponent extends React.Component { + // highlight-range{1-3} + state = { + subscribedValue: this.props.dataSource.value, + }; + // highlight-line + // highlight-range{1-3} + componentDidMount() { + this.finalizeSubscription(); + } + // highlight-line + // highlight-range{1-9} + componentDidUpdate(prevProps, prevState) { + if (this.props.dataSource !== prevProps.dataSource) { + // Similar to adding subscriptions, + // It's only safe to unsubscribe during the commit phase. + prevProps.dataSource.dispose(); + + this.finalizeSubscription(); + } + } + + componentWillUnmount() { + this.props.dataSource.unsubscribe( + this.handleSubscriptionChange + ); + } + + // highlight-range{1-18} + finalizeSubscription() { + // Event listeners are only safe to add during the commit phase, + // So they won't leak if render 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, + }); + } + } + + handleSubscriptionChange = subscribedValue => { + this.setState({subscribedValue}); + }; +} From c45fb40ff3b604964d018203360821ed2beae2a8 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 20 Feb 2018 17:02:43 -0800 Subject: [PATCH 28/61] Triggering rebuild of Netlify From 858c1a751115cd22247ddd3aeabb3cb7b0a024e3 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 20 Feb 2018 17:05:29 -0800 Subject: [PATCH 29/61] Fixed small error in example --- .../adding-event-listeners-after-continued.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/update-on-async-rendering/adding-event-listeners-after-continued.js b/examples/update-on-async-rendering/adding-event-listeners-after-continued.js index 9ba78f20e34..6241c021212 100644 --- a/examples/update-on-async-rendering/adding-event-listeners-after-continued.js +++ b/examples/update-on-async-rendering/adding-event-listeners-after-continued.js @@ -10,12 +10,14 @@ class ExampleComponent extends React.Component { this.finalizeSubscription(); } // highlight-line - // highlight-range{1-9} + // highlight-range{1-11} componentDidUpdate(prevProps, prevState) { if (this.props.dataSource !== prevProps.dataSource) { // Similar to adding subscriptions, // It's only safe to unsubscribe during the commit phase. - prevProps.dataSource.dispose(); + prevProps.dataSource.unsubscribe( + this.handleSubscriptionChange + ); this.finalizeSubscription(); } From 98d5a092afbd3b2bcfb03a8b54da043c81db57a6 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 20 Feb 2018 17:09:43 -0800 Subject: [PATCH 30/61] Added gDSFP to example --- .../adding-event-listeners-after-continued.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/examples/update-on-async-rendering/adding-event-listeners-after-continued.js b/examples/update-on-async-rendering/adding-event-listeners-after-continued.js index 6241c021212..d8516644b96 100644 --- a/examples/update-on-async-rendering/adding-event-listeners-after-continued.js +++ b/examples/update-on-async-rendering/adding-event-listeners-after-continued.js @@ -5,6 +5,18 @@ class ExampleComponent extends React.Component { subscribedValue: this.props.dataSource.value, }; // highlight-line + // highlight-range{1-10} + static getDerivedStateFromProps(nextProps, prevState) { + if ( + prevState.subscribedValue !== + nextProps.dataSource.value + ) { + return { + subscribedValue: nextProps.dataSource.value, + }; + } + } + // highlight-line // highlight-range{1-3} componentDidMount() { this.finalizeSubscription(); From 442591cec9159ca32c5e84473adc227f2785cdb9 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 22 Feb 2018 13:19:39 -0800 Subject: [PATCH 31/61] Added example of updating subscription from props The nuance of this particular example came up while updating react-relay --- .../2018-02-07-update-on-async-rendering.md | 12 ++++- .../adding-event-listeners-after.js | 6 ++- .../adding-event-listeners-before.js | 6 ++- ...-subscriptions-when-props-change-after.js} | 46 +++++++++++-------- 4 files changed, 44 insertions(+), 26 deletions(-) rename examples/update-on-async-rendering/{adding-event-listeners-after-continued.js => updating-subscriptions-when-props-change-after.js} (54%) diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index f7cfeba3dca..b076e7b1da1 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -105,8 +105,16 @@ For this reason, the recommended way to add listeners/subscriptions is to use th > > Although the pattern above is slightly more verbose, it has an added benefit of deferring the subscription creation until after the component has rendered, reducing the amount of time in the critical render path. In the near future, React may include more tools to reduce code complexity for data fetching cases like this. -You may also need to update subscriptions based on changes in `props`. In this case, you should also wait to _remove_ a subscription until `componentDidUpdate`. For example: -`embed:update-on-async-rendering/adding-event-listeners-after-continued.js` +### Updating subscriptions when `props` change + +(This is a continuation of [the example above](#adding-event-listeners-or-subscriptions).) + +You may also need to update subscriptions based on changes in `props`. In this case, you should also wait until `componentDidUpdate` before _removing_ a subscription. In the event that a render is cancelled before being committed, this will prevent us from unsubscribing prematurely. + +Howeveer, waiting to unsubscribe means that we will need to be careful about how we handle events that are dispatched in between `getDerivedStateFromProps` and `componentDidUpdate` so that we don't put stale values into the `state`. To do this, we should use the callback form of `setState` and compare the event dispatcher to the one currently in `state`. + +Here is a example: +`embed:update-on-async-rendering/updating-subscriptions-when-props-change-after.js` ### Updating `state` based on `props` diff --git a/examples/update-on-async-rendering/adding-event-listeners-after.js b/examples/update-on-async-rendering/adding-event-listeners-after.js index e6018da5e4d..3456732cb11 100644 --- a/examples/update-on-async-rendering/adding-event-listeners-after.js +++ b/examples/update-on-async-rendering/adding-event-listeners-after.js @@ -31,7 +31,9 @@ class ExampleComponent extends React.Component { ); } - handleSubscriptionChange = subscribedValue => { - this.setState({subscribedValue}); + handleSubscriptionChange = dataSource => { + this.setState({ + subscribedValue: dataSource.value, + }); }; } diff --git a/examples/update-on-async-rendering/adding-event-listeners-before.js b/examples/update-on-async-rendering/adding-event-listeners-before.js index c83da8a1c76..ac0574dba0d 100644 --- a/examples/update-on-async-rendering/adding-event-listeners-before.js +++ b/examples/update-on-async-rendering/adding-event-listeners-before.js @@ -18,7 +18,9 @@ class ExampleComponent extends React.Component { ); } - handleSubscriptionChange = subscribedValue => { - this.setState({subscribedValue}); + handleSubscriptionChange = dataSource => { + this.setState({ + subscribedValue: dataSource.value, + }); }; } diff --git a/examples/update-on-async-rendering/adding-event-listeners-after-continued.js b/examples/update-on-async-rendering/updating-subscriptions-when-props-change-after.js similarity index 54% rename from examples/update-on-async-rendering/adding-event-listeners-after-continued.js rename to examples/update-on-async-rendering/updating-subscriptions-when-props-change-after.js index d8516644b96..f9f81ed4947 100644 --- a/examples/update-on-async-rendering/adding-event-listeners-after-continued.js +++ b/examples/update-on-async-rendering/updating-subscriptions-when-props-change-after.js @@ -1,17 +1,16 @@ // After class ExampleComponent extends React.Component { - // highlight-range{1-3} + // highlight-range{1-4} state = { + dataSource: this.props.dataSource, subscribedValue: this.props.dataSource.value, }; // highlight-line - // highlight-range{1-10} + // highlight-range{1-8} static getDerivedStateFromProps(nextProps, prevState) { - if ( - prevState.subscribedValue !== - nextProps.dataSource.value - ) { + if (nextProps.dataSource !== prevState.dataSource) { return { + dataSource: nextProps.dataSource, subscribedValue: nextProps.dataSource.value, }; } @@ -24,10 +23,10 @@ class ExampleComponent extends React.Component { // highlight-line // highlight-range{1-11} componentDidUpdate(prevProps, prevState) { - if (this.props.dataSource !== prevProps.dataSource) { + if (this.state.dataSource !== prevState.dataSource) { // Similar to adding subscriptions, // It's only safe to unsubscribe during the commit phase. - prevProps.dataSource.unsubscribe( + prevState.dataSource.unsubscribe( this.handleSubscriptionChange ); @@ -36,32 +35,39 @@ class ExampleComponent extends React.Component { } componentWillUnmount() { - this.props.dataSource.unsubscribe( + this.state.dataSource.unsubscribe( this.handleSubscriptionChange ); } - // highlight-range{1-18} + // highlight-range{1-14} finalizeSubscription() { // Event listeners are only safe to add during the commit phase, // So they won't leak if render is interrupted or errors. - this.props.dataSource.subscribe( + this.state.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, - }); + const subscribedValue = this.state.dataSource.value; + if (subscribedValue !== this.state.subscribedValue) { + this.setState({subscribedValue}); } } + // highlight-line + // highlight-range{1-13} + handleSubscriptionChange = dataSource => { + this.setState(state => { + // If this event belongs to the current data source, update. + // Otherwise we should ignore it. + if (dataSource === state.dataSource) { + return { + subscribedValue: dataSource.value, + }; + } - handleSubscriptionChange = subscribedValue => { - this.setState({subscribedValue}); + return null; + }); }; } From b1ce57229e710bf4e38e8509ed079c916a2e8776 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 22 Feb 2018 16:54:02 -0800 Subject: [PATCH 32/61] Typo --- content/blog/2018-02-07-update-on-async-rendering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index b076e7b1da1..52a5f068efb 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -111,7 +111,7 @@ For this reason, the recommended way to add listeners/subscriptions is to use th You may also need to update subscriptions based on changes in `props`. In this case, you should also wait until `componentDidUpdate` before _removing_ a subscription. In the event that a render is cancelled before being committed, this will prevent us from unsubscribing prematurely. -Howeveer, waiting to unsubscribe means that we will need to be careful about how we handle events that are dispatched in between `getDerivedStateFromProps` and `componentDidUpdate` so that we don't put stale values into the `state`. To do this, we should use the callback form of `setState` and compare the event dispatcher to the one currently in `state`. +However, waiting to unsubscribe means that we will need to be careful about how we handle events that are dispatched in between `getDerivedStateFromProps` and `componentDidUpdate` so that we don't put stale values into the `state`. To do this, we should use the callback form of `setState` and compare the event dispatcher to the one currently in `state`. Here is a example: `embed:update-on-async-rendering/updating-subscriptions-when-props-change-after.js` From 2312173e669ac5c79196f816e621ae49e7d75332 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 28 Feb 2018 09:10:34 -0800 Subject: [PATCH 33/61] Moved updating-subscription example into an external Gist with a note --- .../2018-02-07-update-on-async-rendering.md | 23 ++---- ...g-subscriptions-when-props-change-after.js | 73 ------------------- 2 files changed, 8 insertions(+), 88 deletions(-) delete mode 100644 examples/update-on-async-rendering/updating-subscriptions-when-props-change-after.js diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index 52a5f068efb..471f5542514 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -25,7 +25,7 @@ In this post, we will explore some of the potential capabilities of async render We have been fine-tuning the performance of React with every new release. However, despite what synthetic benchmarks say, we've found that the bottleneck in real-world apps is generally not React itself, but the application code that uses it. In order to unlock the next wave of performance optimizations and new features, we need React to be smarter about when to re-render components and flush updates to the screen. -> **Note** +> Note > > React already has some optimizations in this regard. For example, React batches state updates so that if you call `setState` multiple times in quick succession, it only renders once. @@ -85,7 +85,7 @@ The recommended upgrade path for most use cases is to move data-fetching into `c 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: +> 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](https://gist.github.com/bvaughn/89700e525ff423a75ffb63b1b1e30a8f). @@ -101,20 +101,13 @@ People often assume that `componentWillMount` and `componentWillUnmount` are alw For this reason, the recommended way to add listeners/subscriptions is to use the `componentDidMount` lifecycle: `embed:update-on-async-rendering/adding-event-listeners-after.js` -> **Note**: +> Note > > Although the pattern above is slightly more verbose, it has an added benefit of deferring the subscription creation until after the component has rendered, reducing the amount of time in the critical render path. In the near future, React may include more tools to reduce code complexity for data fetching cases like this. -### Updating subscriptions when `props` change - -(This is a continuation of [the example above](#adding-event-listeners-or-subscriptions).) - -You may also need to update subscriptions based on changes in `props`. In this case, you should also wait until `componentDidUpdate` before _removing_ a subscription. In the event that a render is cancelled before being committed, this will prevent us from unsubscribing prematurely. - -However, waiting to unsubscribe means that we will need to be careful about how we handle events that are dispatched in between `getDerivedStateFromProps` and `componentDidUpdate` so that we don't put stale values into the `state`. To do this, we should use the callback form of `setState` and compare the event dispatcher to the one currently in `state`. - -Here is a example: -`embed:update-on-async-rendering/updating-subscriptions-when-props-change-after.js` +> Note +> +> 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. If you're authoring such a library, we suggest using a technique like [the one shown here](https://gist.github.com/bvaughn/d569177d70b50b58bff69c3c4a5353f3). ### Updating `state` based on `props` @@ -126,7 +119,7 @@ Although the above code is not problematic in itself, the `componentWillReceiveP 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.): `embed:update-on-async-rendering/updating-state-from-props-after.js` -> Note: +> Note > > The [`react-lifecycles-compat`](https://github.com/reactjs/react-lifecycles-compat) polyfill enables this new lifecycle to be used with older versions of React as well. [Learn more about how to use it below.](http://localhost:8000/blog/2018/02/07/update-on-async-rendering.html#open-source-project-maintainers) @@ -148,7 +141,7 @@ Here is an example of a component that fetches external data based on `props` va 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: `embed:update-on-async-rendering/updating-external-data-when-props-change-after.js` -> **Note** +> Note > > If you're using an HTTP library that supports cancellation, like [axios](https://www.npmjs.com/package/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](https://gist.github.com/bvaughn/982ab689a41097237f6e9860db7ca8d6). diff --git a/examples/update-on-async-rendering/updating-subscriptions-when-props-change-after.js b/examples/update-on-async-rendering/updating-subscriptions-when-props-change-after.js deleted file mode 100644 index f9f81ed4947..00000000000 --- a/examples/update-on-async-rendering/updating-subscriptions-when-props-change-after.js +++ /dev/null @@ -1,73 +0,0 @@ -// After -class ExampleComponent extends React.Component { - // highlight-range{1-4} - state = { - dataSource: this.props.dataSource, - subscribedValue: this.props.dataSource.value, - }; - // highlight-line - // highlight-range{1-8} - static getDerivedStateFromProps(nextProps, prevState) { - if (nextProps.dataSource !== prevState.dataSource) { - return { - dataSource: nextProps.dataSource, - subscribedValue: nextProps.dataSource.value, - }; - } - } - // highlight-line - // highlight-range{1-3} - componentDidMount() { - this.finalizeSubscription(); - } - // highlight-line - // highlight-range{1-11} - componentDidUpdate(prevProps, prevState) { - if (this.state.dataSource !== prevState.dataSource) { - // Similar to adding subscriptions, - // It's only safe to unsubscribe during the commit phase. - prevState.dataSource.unsubscribe( - this.handleSubscriptionChange - ); - - this.finalizeSubscription(); - } - } - - componentWillUnmount() { - this.state.dataSource.unsubscribe( - this.handleSubscriptionChange - ); - } - - // highlight-range{1-14} - finalizeSubscription() { - // Event listeners are only safe to add during the commit phase, - // So they won't leak if render is interrupted or errors. - this.state.dataSource.subscribe( - this.handleSubscriptionChange - ); - - // External values could change between render and mount, - // In some cases it may be important to handle this case. - const subscribedValue = this.state.dataSource.value; - if (subscribedValue !== this.state.subscribedValue) { - this.setState({subscribedValue}); - } - } - // highlight-line - // highlight-range{1-13} - handleSubscriptionChange = dataSource => { - this.setState(state => { - // If this event belongs to the current data source, update. - // Otherwise we should ignore it. - if (dataSource === state.dataSource) { - return { - subscribedValue: dataSource.value, - }; - } - - return null; - }); - }; -} From 16eb646416bfc073bbb9b201fa7f68ddc443484e Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 28 Feb 2018 12:13:28 -0800 Subject: [PATCH 34/61] Fixed typo --- content/blog/2018-02-07-update-on-async-rendering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index 471f5542514..8df8b654dfd 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -50,7 +50,7 @@ However, if you'd like to start using the new component API (or if you're a main Before we begin, here's a quick reminder of the lifecyle changes in 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 introducinc a new, static lifecycle, `getDerivedStateFromProps`: +* We are introducing a new, static lifecycle, `getDerivedStateFromProps`: ```js static getDerivedStateFromProps( From 4d16523db18736e5a6a4f20e8128a0d2c0f7b702 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 28 Feb 2018 13:00:17 -0800 Subject: [PATCH 35/61] Typo --- content/blog/2018-02-07-update-on-async-rendering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-02-07-update-on-async-rendering.md index 8df8b654dfd..066099b7729 100644 --- a/content/blog/2018-02-07-update-on-async-rendering.md +++ b/content/blog/2018-02-07-update-on-async-rendering.md @@ -48,7 +48,7 @@ However, if you'd like to start using the new component API (or if you're a main --- -Before we begin, here's a quick reminder of the lifecyle changes in version 16.3: +Before we begin, here's a quick reminder of the lifecycle changes in 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 a new, static lifecycle, `getDerivedStateFromProps`: From 1ca6cfcf3da316cb51e1fe556944b922526f59ef Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 7 Mar 2018 11:14:01 -0800 Subject: [PATCH 36/61] Moved async update blog post to a later, random date --- ...async-rendering.md => 2018-03-15-update-on-async-rendering.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename content/blog/{2018-02-07-update-on-async-rendering.md => 2018-03-15-update-on-async-rendering.md} (100%) diff --git a/content/blog/2018-02-07-update-on-async-rendering.md b/content/blog/2018-03-15-update-on-async-rendering.md similarity index 100% rename from content/blog/2018-02-07-update-on-async-rendering.md rename to content/blog/2018-03-15-update-on-async-rendering.md From 7408e07b62c2f6976a14ce1d08161b8a92039189 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 7 Mar 2018 11:17:02 -0800 Subject: [PATCH 37/61] Remoaved 'What can asynchronous rendering do?' section and instead linked to 'Sneak peek at async' which shows Dan's video --- .../2018-03-15-update-on-async-rendering.md | 25 ++----------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/content/blog/2018-03-15-update-on-async-rendering.md b/content/blog/2018-03-15-update-on-async-rendering.md index 066099b7729..c3ae96b2337 100644 --- a/content/blog/2018-03-15-update-on-async-rendering.md +++ b/content/blog/2018-03-15-update-on-async-rendering.md @@ -1,9 +1,9 @@ --- title: Update on Async Rendering -author: [bvaughn, gaearon] +author: [bvaughn] --- -For the past few months, the React team has been experimenting with [asynchronous rendering](/blog/2017/09/26/react-v16.0.html#new-core-architecture), and we are very excited about the new features it enables. +For the past few months, the React team has been experimenting with [asynchronous rendering](/blog/2018/03/01/sneak-peek-beyond-react-16.html), and we are very excited about the new features it enables. Along the way, our research has shown that some of our component lifecycles tend to encourage unsafe coding practices. They are: @@ -19,27 +19,6 @@ These lifecycle methods have often been misunderstood and subtly misused; furthe In this post, we will explore some of the potential capabilities of async rendering, and we'll outline a migration plan for components that rely on these legacy lifecycles. -## What can asynchronous rendering do? - -#### With every new version, our goal is to make it easier for developers using React to build great user experiences - -We have been fine-tuning the performance of React with every new release. However, despite what synthetic benchmarks say, we've found that the bottleneck in real-world apps is generally not React itself, but the application code that uses it. In order to unlock the next wave of performance optimizations and new features, we need React to be smarter about when to re-render components and flush updates to the screen. - -> Note -> -> React already has some optimizations in this regard. For example, React batches state updates so that if you call `setState` multiple times in quick succession, it only renders once. - -We found that asynchronous rendering can help in several ways. For example: - -1. As users navigate within an app, newly displayed components often have asynchronous dependencies (including data, images, and code splitting). This leads to a lot of boilerplate code managing data fetching and displaying the loading states. It can also lead to a "cascade of spinners" as the data loads, causing DOM reflows and janky user experience. We'd like to make it easier for product developers to express asynchronous dependencies and to wait to show a component until all of its data has been loaded. React could keep the old UI "alive" and interactive for a certain period while the updated UI is not ready yet, and provide a declarative way to show a loading indicator if it takes more than a second. -2. Fast updates within a short timeframe often cause jank because React processes each update individually. We'd like to automatically "combine" updates within a few hundred milliseconds when possible so that there is less re-rendering. -3. Some updates are inherently less important than others. For example, if you're writing a [live-updating search filter input](https://zeit.co/blog/domains-search-web#asynchronous-rendering), it is essential that the input is updated immediately (within a few milliseconds). Re-rendering the result list can be done later, and should not block the thread or cause stutter when typing. It would be nice if React had a way to mark the latter updates as having a lower priority. (Note that even debouncing the input doesn't help because if the rendering is synchronous (like in React today)—a keystroke can't interrupt the rendering once it has started. Asynchronous rendering solves this by splitting rendering into small chunks that can be paused and later resumed.) -4. For UI elements like hidden popups and tabs, we'd like to be able to start pre-rendering their content when the browser isn't busy. This way, they can appear instantaneously in response to a later user interaction. However, we'd like to do this only [when the browser is idle](https://developers.google.com/web/updates/2015/08/using-requestidlecallback) to avoid slowing down other parts of the page. - -Of course, it's possible to implement some of these features today, but it's difficult. We hope to make them effortless by building them into React itself. By replacing problematic lifecycles with safer alternatives, we also hope to make it simple to write async-safe React components. - -In the next section, we'll look at how to update your existing components to prepare for the upcoming lifecycle changes. - ## Updating class components #### If you're an application developer, **you don't have to do anything about the legacy methods yet**. The primary purpose of the upcoming version 16.3 is to enable open source project maintainers to update their libraries in advance of any deprecation warnings. Those warnings will be enabled with the next minor release, version 16.4. From 3c75def7e7af9fc01ca0a0aeca6e20caa235c0a6 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 7 Mar 2018 11:42:10 -0800 Subject: [PATCH 38/61] Deleted StrictMode and examples/images --- content/docs/strict-mode.md | 88 ------------------ .../strict-mode-unsafe-lifecycles-warning.png | Bin 53348 -> 0 bytes examples/16-3-release-blog-create-ref.js | 14 --- .../enabling-strict-mode.js | 18 ---- .../side-effects-in-constructor.js | 7 -- 5 files changed, 127 deletions(-) delete mode 100644 content/docs/strict-mode.md delete mode 100644 content/images/blog/strict-mode-unsafe-lifecycles-warning.png delete mode 100644 examples/16-3-release-blog-create-ref.js delete mode 100644 examples/update-on-async-rendering/enabling-strict-mode.js delete mode 100644 examples/update-on-async-rendering/side-effects-in-constructor.js diff --git a/content/docs/strict-mode.md b/content/docs/strict-mode.md deleted file mode 100644 index 1fc7944a739..00000000000 --- a/content/docs/strict-mode.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -id: strict-mode -title: StrictMode -permalink: docs/strict-mode.html ---- - -`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: -> -> Strict mode checks are run in development mode only; _they do not impact the production build_. - -You can enable strict mode for any part of your application. For example: -`embed:update-on-async-rendering/enabling-strict-mode.js` - -In the above example, strict mode checks will *not* be run against the `Header` and `Footer` components. However, `ComponentOne` and `ComponentTwo`, as well as all of their descendants, will have the checks. - -`StrictMode` currently helps with: -* [Identifying components with unsafe lifecycles](#identifying-unsafe-lifecycles) -* [Warning about legacy string ref API usage](#warning-about-legacy-string-ref-api-usage) -* [Detecting unexpected side effects](#detecting-unexpected-side-effects) - -Additional functionality will be added with future releases of React. - -### Identifying unsafe lifecycles - -As explained [in this blog post](/blog/2018/02/07/update-on-async-rendering.html), certain legacy lifecycle methods are unsafe for use in async React applications. However, if your application uses third party libraries, it can be difficult to ensure that these lifecycles aren't being used. Fortunately, strict mode can help with this! - -When strict mode is enabled, React compiles a list of all class components using the unsafe lifecycles, and logs a warning message with information about these components, like so: - -![](../images/blog/strict-mode-unsafe-lifecycles-warning.png) - -Addressing the issues identified by strict mode _now_ will make it easier for you to take advantage of async rendering in future releases of React. - -### Warning about legacy string ref API usage - -Previously, React provided two ways for 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](https://github.com/facebook/react/issues/1373) and so our official recomendation was to [use the callback form instead](https://reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs). - -React 16.3 added a third option that offers the convenience of a string ref without any of the downsides: -`embed:16-3-release-blog-create-ref.js` - -Since object refs were largely added as a replacement for string refs, strict mode now warns about usage of string refs. - -> **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.](/docs/refs-and-the-dom.html) - -### Detecting unexpected side effects - -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 commit phase is usually very fast, but rendering can be slow. For this reason, the upcoming async mode (which is not enabled by default yet) breaks the rendering work into pieces, pausing and resuming the work to avoid blocking the browser. This means that React may invoke render phase lifecycles more than once before committing, or it may invoke them without committing at all (because of an error or a higher priority interruption). - -Render phase lifecycles include the following class component methods: -* `constructor` -* `componentWillMount` -* `componentWillReceiveProps` -* `componentWillUpdate` -* `getDerivedStateFromProps` -* `shouldComponentUpdate` -* `render` -* `setState` updater functions (the first argument) - -Because the above methods might be called more than once, it's important that they do not contain side-effects. Ignoring this rule can lead to a variety of problems, including memory leaks and invalid application state. Unfortunately, it can be difficult to detect these problems as they can often be [non-deterministic](https://en.wikipedia.org/wiki/Deterministic_algorithm). - -Strict mode can't automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following methods: - -* Class component `constructor` method -* The `render` method -* `setState` updater functions (the first argument) -* The static `getDerivedStateFromProps` lifecycle - -> Note: -> -> This only applies to development mode. _Lifecycles will not be double-invoked in production mode._ - -For example, consider the following code: -`embed:update-on-async-rendering/side-effects-in-constructor.js` - -At first glance, this code might not seem problematic. But if `SharedApplicationState.recordEvent` is not [idempotent](https://en.wikipedia.org/wiki/Idempotence#Computer_science_meaning), then instantiating this component multiple times could lead to invalid application state. This sort of subtle bug might not manifest during development, or it might do so inconsistently and so be overlooked. - -By intentionally double-invoking methods like the component constructor, strict mode makes patterns like this easier to spot. diff --git a/content/images/blog/strict-mode-unsafe-lifecycles-warning.png b/content/images/blog/strict-mode-unsafe-lifecycles-warning.png deleted file mode 100644 index fbdeccde6bec8f2fbb8d8c8db9c4f87d7d395c82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53348 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 axo^$0_?r`rnMWwd>w}b%WVyIu!2buI6T3wK diff --git a/examples/16-3-release-blog-create-ref.js b/examples/16-3-release-blog-create-ref.js deleted file mode 100644 index 77cbb40a70d..00000000000 --- a/examples/16-3-release-blog-create-ref.js +++ /dev/null @@ -1,14 +0,0 @@ -class MyComponent extends React.Component { - // highlight-next-line - divRef = React.createRef(); - - render() { - // highlight-next-line - return ; - } - - componentDidMount() { - // highlight-next-line - this.divRef.value.focus(); - } -} diff --git a/examples/update-on-async-rendering/enabling-strict-mode.js b/examples/update-on-async-rendering/enabling-strict-mode.js deleted file mode 100644 index 7ff62591960..00000000000 --- a/examples/update-on-async-rendering/enabling-strict-mode.js +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; - -function ExampleApplication() { - return ( -
-
- {/* highlight-next-line */} - -
- - -
- {/* highlight-next-line */} -
-
-
- ); -} diff --git a/examples/update-on-async-rendering/side-effects-in-constructor.js b/examples/update-on-async-rendering/side-effects-in-constructor.js deleted file mode 100644 index f0686a4bb50..00000000000 --- a/examples/update-on-async-rendering/side-effects-in-constructor.js +++ /dev/null @@ -1,7 +0,0 @@ -class TopLevelRoute extends React.Component { - constructor(props) { - super(props); - - SharedApplicationState.recordEvent('ExampleComponent'); - } -} From 55650fc12c6902826efcdccba076c45decb884c4 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 15 Mar 2018 08:48:01 -0700 Subject: [PATCH 39/61] Added explicit null value in state initializer --- .../updating-state-from-props-after.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/update-on-async-rendering/updating-state-from-props-after.js b/examples/update-on-async-rendering/updating-state-from-props-after.js index dc5eccded22..fe5de41165d 100644 --- a/examples/update-on-async-rendering/updating-state-from-props-after.js +++ b/examples/update-on-async-rendering/updating-state-from-props-after.js @@ -2,18 +2,19 @@ class ExampleComponent extends React.Component { // Initialize state in constructor, // Or with a property initializer. - // highlight-range{1-3} + // highlight-range{1-4} state = { isScrollingDown: false, + lastRow: null, }; // highlight-line // highlight-range{1-8} static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.currentRow !== prevState.lastRow) { return { - lastRow: nextProps.currentRow, isScrollingDown: nextProps.currentRow > prevState.lastRow, + lastRow: nextProps.currentRow, }; } From 97a109d8b20ac89f5e456766072b3d81320f53a6 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 15 Mar 2018 11:24:04 -0700 Subject: [PATCH 40/61] Added a note about experimental class properties usage in examples --- content/blog/2018-03-15-update-on-async-rendering.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/content/blog/2018-03-15-update-on-async-rendering.md b/content/blog/2018-03-15-update-on-async-rendering.md index c3ae96b2337..360f4a1a1fa 100644 --- a/content/blog/2018-03-15-update-on-async-rendering.md +++ b/content/blog/2018-03-15-update-on-async-rendering.md @@ -44,6 +44,10 @@ This new lifecycle is invoked after a component is instantiated and when it rece Now let's take a look at some 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`: From 21fa1162b631eff38295ffddf79a77a00022b875 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 16 Mar 2018 16:24:45 -0700 Subject: [PATCH 41/61] Removed StrictMode from side nav --- content/docs/nav.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/content/docs/nav.yml b/content/docs/nav.yml index cb6fbdb417e..8bbadf66a84 100644 --- a/content/docs/nav.yml +++ b/content/docs/nav.yml @@ -74,8 +74,6 @@ title: Accessibility - id: code-splitting title: Code-Splitting - - id: strict-mode - title: StrictMode - title: Reference items: - id: react-api From 92cf72dafdd89c5ad6df1a0ca3ac5014ac50cb84 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 20 Mar 2018 10:36:58 -0700 Subject: [PATCH 42/61] Hardened wording a bit around async --- content/blog/2018-03-15-update-on-async-rendering.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/content/blog/2018-03-15-update-on-async-rendering.md b/content/blog/2018-03-15-update-on-async-rendering.md index 360f4a1a1fa..144b3d58c66 100644 --- a/content/blog/2018-03-15-update-on-async-rendering.md +++ b/content/blog/2018-03-15-update-on-async-rendering.md @@ -3,15 +3,17 @@ title: Update on Async Rendering author: [bvaughn] --- -For the past few months, the React team has been experimenting with [asynchronous rendering](/blog/2018/03/01/sneak-peek-beyond-react-16.html), and we are very excited about the new features it enables. +For the past few months, the React team has been working on [asynchronous rendering](/blog/2018/03/01/sneak-peek-beyond-react-16.html), and we are very excited about the new features it enables. -Along the way, our research has shown that some of our component lifecycles tend to encourage unsafe coding practices. They are: +Along the way, we've learned 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 these potential problems may be more prominent with async rendering. Because of this, we are adding an "UNSAFE_" prefix to these lifecycles in a future release. React [follows semantic versioning](/blog/2016/02/19/new-versioning-scheme.html), so the migration path will be gradual: +These lifecycle methods have often been misunderstood and subtly misused; furthermore, we anticipate that these potential problems may be more prominent with async rendering. Because of this, we are adding an "UNSAFE_" prefix to these lifecycles in a future release. + +[React follows semantic versioning](/blog/2016/02/19/new-versioning-scheme.html), so the migration path will be gradual: * **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.) * **16.4**: Enable deprecation warning for `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate`. (Both the old lifecycle names and the new aliases will work in this release.) From fa34fcf5825fe339f9c68334898dd977910b139e Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 22 Mar 2018 08:09:21 -0700 Subject: [PATCH 43/61] 16.4 -> 16.x --- content/blog/2018-03-15-update-on-async-rendering.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/blog/2018-03-15-update-on-async-rendering.md b/content/blog/2018-03-15-update-on-async-rendering.md index 144b3d58c66..a85a9f88d61 100644 --- a/content/blog/2018-03-15-update-on-async-rendering.md +++ b/content/blog/2018-03-15-update-on-async-rendering.md @@ -16,14 +16,14 @@ These lifecycle methods have often been misunderstood and subtly misused; furthe [React follows semantic versioning](/blog/2016/02/19/new-versioning-scheme.html), so the migration path will be gradual: * **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.) -* **16.4**: Enable deprecation warning for `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate`. (Both the old lifecycle names and the new aliases will work in this release.) +* **16.x**: Enable deprecation warning for `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate`. (Both the old lifecycle names and the new aliases will work in this release.) * **17.0**: Remove `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate` . (Only the new "UNSAFE_" lifecycle names will work in this release.) In this post, we will explore some of the potential capabilities of async rendering, and we'll outline a migration plan for components that rely on these legacy lifecycles. ## Updating class components -#### If you're an application developer, **you don't have to do anything about the legacy methods yet**. The primary purpose of the upcoming version 16.3 is to enable open source project maintainers to update their libraries in advance of any deprecation warnings. Those warnings will be enabled with the next minor release, version 16.4. +#### If you're an application developer, **you don't have to do anything about the legacy methods yet**. The primary purpose of the upcoming version 16.3 is to enable open source project maintainers to update their libraries in advance of any deprecation warnings. Those warnings will be enabled with a future 16.x release. However, if you'd like to start using the new component API (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. From b3bf0bd520688cfdfd6bc9866a6e0a62f637611f Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 23 Mar 2018 11:23:07 -0700 Subject: [PATCH 44/61] Added getSnapshotBeforeUpdate recipe --- .../2018-03-15-update-on-async-rendering.md | 30 ++++++++++------ .../new-lifecycles-overview.js | 19 ++++++++++ ...eact-dom-properties-before-update-after.js | 35 ++++++++++++++++++ ...act-dom-properties-before-update-before.js | 36 +++++++++++++++++++ 4 files changed, 109 insertions(+), 11 deletions(-) create mode 100644 examples/update-on-async-rendering/new-lifecycles-overview.js create mode 100644 examples/update-on-async-rendering/react-dom-properties-before-update-after.js create mode 100644 examples/update-on-async-rendering/react-dom-properties-before-update-before.js diff --git a/content/blog/2018-03-15-update-on-async-rendering.md b/content/blog/2018-03-15-update-on-async-rendering.md index a85a9f88d61..8bf43470ee7 100644 --- a/content/blog/2018-03-15-update-on-async-rendering.md +++ b/content/blog/2018-03-15-update-on-async-rendering.md @@ -29,27 +29,24 @@ However, if you'd like to start using the new component API (or if you're a main --- -Before we begin, here's a quick reminder of the lifecycle changes in version 16.3: +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 a new, static lifecycle, `getDerivedStateFromProps`: +* We are introducing two new lifecycles, static `getDerivedStateFromProps` and `getSnapshotBeforeUpdate`: -```js -static getDerivedStateFromProps( - nextProps: Props, - prevState: State -): $Shape | null -``` +`embed:update-on-async-rendering/new-lifecycles-overview.js` -This new lifecycle is invoked after a component is instantiated and when it receives new props. It should return an object to update `state`, or `null` to indicate that the new `props` do not require any `state` updates. +The new static `getSnapshotBeforeUpdate` lifecycle is invoked after a component is instantiated as well as when it receives new props. It should return an object to update `state`, or `null` to indicate that the new `props` do not require any `state` updates. ---- +The new `getSnapshotBeforeUpdate` lifecycle gets 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`. -Now let's take a look at some examples. +We'll look at examples of how both of these lifecycles can be used below. > 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`: @@ -130,6 +127,17 @@ The recommended upgrade path for this component is to move data-updates into `co > > If you're using an HTTP library that supports cancellation, like [axios](https://www.npmjs.com/package/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](https://gist.github.com/bvaughn/982ab689a41097237f6e9860db7ca8d6). +### 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: +`embed:update-on-async-rendering/react-dom-properties-before-update-before.js` + +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`). A user might continue scrolling during the delay, in which case the position 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. + +`embed:update-on-async-rendering/react-dom-properties-before-update-after.js` + ## 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](https://github.com/reactjs/reactjs.org/issues/new) 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. diff --git a/examples/update-on-async-rendering/new-lifecycles-overview.js b/examples/update-on-async-rendering/new-lifecycles-overview.js new file mode 100644 index 00000000000..4bf67772a53 --- /dev/null +++ b/examples/update-on-async-rendering/new-lifecycles-overview.js @@ -0,0 +1,19 @@ +class Example extends React.Component< + Props, + State, + Snapshot +> { + static getDerivedStateFromProps( + nextProps: Props, + prevState: State + ): $Shape | null { + // ... + } + + getSnapshotBeforeUpdate( + prevProps: Props, + prevState: State + ): Snapshot { + // ... + } +} diff --git a/examples/update-on-async-rendering/react-dom-properties-before-update-after.js b/examples/update-on-async-rendering/react-dom-properties-before-update-after.js new file mode 100644 index 00000000000..7f2ead716a1 --- /dev/null +++ b/examples/update-on-async-rendering/react-dom-properties-before-update-after.js @@ -0,0 +1,35 @@ +class ScrollingList extends React.Component { + listRef = null; + + // highlight-range{1-8} + 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; + } + + // highlight-range{1-8} + 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. + if (snapshot !== null) { + this.listRef.scrollTop += + this.listRef.scrollHeight - snapshot; + } + } + + render() { + return ( +
+ {/* ...contents... */} +
+ ); + } + + setListRef = ref => { + this.listRef = ref; + }; +} diff --git a/examples/update-on-async-rendering/react-dom-properties-before-update-before.js b/examples/update-on-async-rendering/react-dom-properties-before-update-before.js new file mode 100644 index 00000000000..a14fcc6b1aa --- /dev/null +++ b/examples/update-on-async-rendering/react-dom-properties-before-update-before.js @@ -0,0 +1,36 @@ +class ScrollingList extends React.Component { + listRef = null; + prevScrollHeight = null; + + // highlight-range{1-7} + 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.prevScrollHeight = this.listRef.scrollHeight; + } + } + + // highlight-range{1-9} + componentDidUpdate(prevProps, prevState) { + // If prevScrollHeight 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.prevScrollHeight !== null) { + this.listRef.scrollTop += + this.listRef.scrollHeight - this.prevScrollHeight; + this.prevScrollHeight = null; + } + } + + render() { + return ( +
+ {/* ...contents... */} +
+ ); + } + + setListRef = ref => { + this.listRef = ref; + }; +} From 254fc8b064d91d89992b8c3788f05eefb5c96733 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 23 Mar 2018 11:45:02 -0700 Subject: [PATCH 45/61] Wordsmithing nits --- content/blog/2018-03-15-update-on-async-rendering.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/content/blog/2018-03-15-update-on-async-rendering.md b/content/blog/2018-03-15-update-on-async-rendering.md index 8bf43470ee7..192182bc81d 100644 --- a/content/blog/2018-03-15-update-on-async-rendering.md +++ b/content/blog/2018-03-15-update-on-async-rendering.md @@ -132,10 +132,12 @@ The recommended upgrade path for this component is to move data-updates into `co 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: `embed:update-on-async-rendering/react-dom-properties-before-update-before.js` -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`). A user might continue scrolling during the delay, in which case the position value read from `componentWillUpdate` will be stale. +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`). A user might continue scrolling during this delay, in which case the position 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: + `embed:update-on-async-rendering/react-dom-properties-before-update-after.js` ## Other scenarios From b0c22f75eff54b65d2f9a60e1cad2e547ae81b21 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 23 Mar 2018 12:48:11 -0700 Subject: [PATCH 46/61] Wording tweak --- content/blog/2018-03-15-update-on-async-rendering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2018-03-15-update-on-async-rendering.md b/content/blog/2018-03-15-update-on-async-rendering.md index 192182bc81d..16287571d1a 100644 --- a/content/blog/2018-03-15-update-on-async-rendering.md +++ b/content/blog/2018-03-15-update-on-async-rendering.md @@ -132,7 +132,7 @@ The recommended upgrade path for this component is to move data-updates into `co 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: `embed:update-on-async-rendering/react-dom-properties-before-update-before.js` -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`). A user might continue scrolling during this delay, in which case the position value read from `componentWillUpdate` will be stale. +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. From 558d576818489407646ccb646cfaa359acc06868 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sat, 24 Mar 2018 10:19:00 -0700 Subject: [PATCH 47/61] Reworked introduction --- .../2018-03-15-update-on-async-rendering.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/content/blog/2018-03-15-update-on-async-rendering.md b/content/blog/2018-03-15-update-on-async-rendering.md index 16287571d1a..8def2fd59f0 100644 --- a/content/blog/2018-03-15-update-on-async-rendering.md +++ b/content/blog/2018-03-15-update-on-async-rendering.md @@ -3,27 +3,27 @@ title: Update on Async Rendering author: [bvaughn] --- -For the past few months, the React team has been working on [asynchronous rendering](/blog/2018/03/01/sneak-peek-beyond-react-16.html), and we are very excited about the new features it enables. +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](/blog/2018/03/01/sneak-peek-beyond-react-16.html). Now we'd like to share with you some of the lessons we've learned while working on this feature, and some suggestions we have for preparing your components to be ready for async rendering when it launches. -Along the way, we've learned that some of our legacy component lifecycles tend to encourage unsafe coding practices. They are: +One of the biggest lesson 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 these potential problems may be more prominent with async rendering. Because of this, we are adding an "UNSAFE_" prefix to these lifecycles in a future release. +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. -[React follows semantic versioning](/blog/2016/02/19/new-versioning-scheme.html), so the migration path will be gradual: +[React follows semantic versioning](/blog/2016/02/19/new-versioning-scheme.html), 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.) -* **16.x**: Enable deprecation warning for `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate`. (Both the old lifecycle names and the new aliases will work in this release.) -* **17.0**: Remove `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate` . (Only the new "UNSAFE_" lifecycle names will work in this release.) +* **16.x**: 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.) -In this post, we will explore some of the potential capabilities of async rendering, and we'll outline a migration plan for components that rely on these legacy lifecycles. +Below, we will outline a migration plan for components that rely on these legacy lifecycles. ## Updating class components -#### If you're an application developer, **you don't have to do anything about the legacy methods yet**. The primary purpose of the upcoming version 16.3 is to enable open source project maintainers to update their libraries in advance of any deprecation warnings. Those warnings will be enabled with a future 16.x release. +#### If you're an 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. However, if you'd like to start using the new component API (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. @@ -35,9 +35,9 @@ Before we begin, here's a quick overview of the lifecycle changes planned for ve `embed:update-on-async-rendering/new-lifecycles-overview.js` -The new static `getSnapshotBeforeUpdate` lifecycle is invoked after a component is instantiated as well as when it receives new props. It should return an object to update `state`, or `null` to indicate that the new `props` do not require any `state` updates. +The new static `getSnapshotBeforeUpdate` 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. -The new `getSnapshotBeforeUpdate` lifecycle gets 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`. +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`. We'll look at examples of how both of these lifecycles can be used below. From 7425aed688e79f4aca359a9765929678408d10a1 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sat, 24 Mar 2018 10:29:12 -0700 Subject: [PATCH 48/61] Typofix --- content/blog/2018-03-15-update-on-async-rendering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2018-03-15-update-on-async-rendering.md b/content/blog/2018-03-15-update-on-async-rendering.md index 8def2fd59f0..aa326cf9d5a 100644 --- a/content/blog/2018-03-15-update-on-async-rendering.md +++ b/content/blog/2018-03-15-update-on-async-rendering.md @@ -5,7 +5,7 @@ author: [bvaughn] 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](/blog/2018/03/01/sneak-peek-beyond-react-16.html). Now we'd like to share with you some of the lessons we've learned while working on this feature, and some suggestions we have for preparing your components to be ready for async rendering when it launches. -One of the biggest lesson we've learned is that some of our legacy component lifecycles tend to encourage unsafe coding practices. They are: +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` From 65b1496d95129f9750c7c2595195a2566a4f3171 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sat, 24 Mar 2018 11:00:06 -0700 Subject: [PATCH 49/61] Typo --- content/blog/2018-03-15-update-on-async-rendering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2018-03-15-update-on-async-rendering.md b/content/blog/2018-03-15-update-on-async-rendering.md index aa326cf9d5a..e1b551056f0 100644 --- a/content/blog/2018-03-15-update-on-async-rendering.md +++ b/content/blog/2018-03-15-update-on-async-rendering.md @@ -35,7 +35,7 @@ Before we begin, here's a quick overview of the lifecycle changes planned for ve `embed:update-on-async-rendering/new-lifecycles-overview.js` -The new static `getSnapshotBeforeUpdate` 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. +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. 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`. From 6eae811789f1345bb7aef093b091705eeedee5cf Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Sat, 24 Mar 2018 18:08:35 +0000 Subject: [PATCH 50/61] Tweaks to async post --- .../2018-03-15-update-on-async-rendering.md | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/content/blog/2018-03-15-update-on-async-rendering.md b/content/blog/2018-03-15-update-on-async-rendering.md index e1b551056f0..45fcdfffa29 100644 --- a/content/blog/2018-03-15-update-on-async-rendering.md +++ b/content/blog/2018-03-15-update-on-async-rendering.md @@ -3,7 +3,7 @@ title: Update on Async Rendering author: [bvaughn] --- -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](/blog/2018/03/01/sneak-peek-beyond-react-16.html). Now we'd like to share with you some of the lessons we've learned while working on this feature, and some suggestions we have for preparing your components to be ready for async rendering when it launches. +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](/blog/2018/03/01/sneak-peek-beyond-react-16.html). 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: @@ -13,32 +13,42 @@ One of the biggest lessons we've learned is that some of our legacy component li 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. +## Gradual Migration Path + [React follows semantic versioning](/blog/2016/02/19/new-versioning-scheme.html), 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.) * **16.x**: 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.) -Below, we will outline a migration plan for components that rely on these legacy lifecycles. +**Note that if you're an 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.** -## Updating class components +At Facebook, we maintain over 50,000 React components, so we're in the same boat as you. We can't rewrite our apps so we will take the gradual migration path together with everyone in the React community. -#### If you're an 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. +--- -However, if you'd like to start using the new component API (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. +## 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`: +* 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`: `embed:update-on-async-rendering/new-lifecycles-overview.js` +### New lifecycle: `getDerivedStateFromProps` + 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` + 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`. +Together with `componentDidUpdate`, this new lifecycle should cover all use cases for the legacy `componentWillUpdate`. + We'll look at examples of how both of these lifecycles can be used below. > Note From a2139ded8a32a7e71e51d8edcf74a502aeb2dde7 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Sat, 24 Mar 2018 18:23:13 +0000 Subject: [PATCH 51/61] Add a note about suspense --- content/blog/2018-03-15-update-on-async-rendering.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/blog/2018-03-15-update-on-async-rendering.md b/content/blog/2018-03-15-update-on-async-rendering.md index 45fcdfffa29..83db28b79d2 100644 --- a/content/blog/2018-03-15-update-on-async-rendering.md +++ b/content/blog/2018-03-15-update-on-async-rendering.md @@ -80,6 +80,8 @@ There is a common misconception that fetching in `componentWillMount` lets you a > 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](https://gist.github.com/bvaughn/89700e525ff423a75ffb63b1b1e30a8f). +> +> 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](/blog/2018/03/01/sneak-peek-beyond-react-16.html). 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 at the moment it is not quite ready yet. ### Adding event listeners (or subscriptions) From e110ac5745dc00f84850e8dc1714bb359dc0a4c5 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sat, 24 Mar 2018 16:01:44 -0700 Subject: [PATCH 52/61] Added a small TOC for examples --- content/blog/2018-03-15-update-on-async-rendering.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/content/blog/2018-03-15-update-on-async-rendering.md b/content/blog/2018-03-15-update-on-async-rendering.md index 83db28b79d2..18e3a63391e 100644 --- a/content/blog/2018-03-15-update-on-async-rendering.md +++ b/content/blog/2018-03-15-update-on-async-rendering.md @@ -55,7 +55,14 @@ We'll look at examples of how both of these lifecycles can be used below. > > For brevity, the examples below are written using the experimental class properties transform, but the same migration strategies apply without it. ---- +## Examples +- [Initializing state](#initializing-state) +- [Fetching external data](#fetching-external-data) +- [Adding event listeners (or subscriptions)](#adding-event-listeners-or-subscriptions) +- [Updating `state` based on props](#updating-state-based-on-props) +- [Invoking external callbacks](#invoking-external-callbacks) +- [Updating external data when props change](#updating-external-data-when-props-change) +- [Reading DOM properties before an update](#reading-dom-properties-before-an-update) ### Initializing state From ce060eb37a35924ee14f898cd911ab7c4fc172f2 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sat, 24 Mar 2018 16:01:57 -0700 Subject: [PATCH 53/61] Imported theme style tweaks from PR 587 --- src/theme.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/theme.js b/src/theme.js index 9da7e3e47b3..5fba925d7ef 100644 --- a/src/theme.js +++ b/src/theme.js @@ -336,7 +336,7 @@ const sharedStyles = { }, '& li': { - marginTop: 20, + marginTop: 10, }, '& li.button-newapp': { @@ -345,6 +345,7 @@ const sharedStyles = { '& ol, & ul': { marginLeft: 20, + marginTop: 10, }, }, From e143823d7a34f8ee9c20fa0dbe53fed074a85dbb Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sat, 24 Mar 2018 16:26:04 -0700 Subject: [PATCH 54/61] Added create-subscription example --- .../2018-03-15-update-on-async-rendering.md | 10 ++++-- ...ing-event-listeners-create-subscription.js | 33 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 examples/update-on-async-rendering/adding-event-listeners-create-subscription.js diff --git a/content/blog/2018-03-15-update-on-async-rendering.md b/content/blog/2018-03-15-update-on-async-rendering.md index 18e3a63391e..e2fe9cf321d 100644 --- a/content/blog/2018-03-15-update-on-async-rendering.md +++ b/content/blog/2018-03-15-update-on-async-rendering.md @@ -106,9 +106,15 @@ For this reason, the recommended way to add listeners/subscriptions is to use th > > Although the pattern above is slightly more verbose, it has an added benefit of deferring the subscription creation until after the component has rendered, reducing the amount of time in the critical render path. In the near future, React may include more tools to reduce code complexity for data fetching cases like this. +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`](https://github.com/facebook/react/tree/master/packages/create-subscription), to help with this. + +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: + +`embed:update-on-async-rendering/adding-event-listeners-create-subscription.js` + > Note -> -> 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. If you're authoring such a library, we suggest using a technique like [the one shown here](https://gist.github.com/bvaughn/d569177d70b50b58bff69c3c4a5353f3). +> +> Libraries like Relay/Apollo should manage subscriptions manually with the same techniques as `create-subscription` uses under the hood (as referenced [here](https://gist.github.com/bvaughn/d569177d70b50b58bff69c3c4a5353f3)) in a way that is most optimized for their library usage. ### Updating `state` based on `props` diff --git a/examples/update-on-async-rendering/adding-event-listeners-create-subscription.js b/examples/update-on-async-rendering/adding-event-listeners-create-subscription.js new file mode 100644 index 00000000000..9b096070a67 --- /dev/null +++ b/examples/update-on-async-rendering/adding-event-listeners-create-subscription.js @@ -0,0 +1,33 @@ +import {createSubscription} from 'create-subscription'; + +const Subscription = createSubscription({ + getCurrentValue(source) { + // Return the current value of the subscription (source). + // highlight-next-line + return source.value; + }, + + subscribe(source, callback) { + function handleSubscriptionChange() { + callback(dataSource.value); + } + + // Subscribe (e.g. add an event listener) to the subscription (source). + // Call callback(newValue) whenever a subscription changes. + // highlight-next-line + source.subscribe(handleSubscriptionChange); + + // Return an unsubscribe method. + // highlight-range{1-3} + return function unsubscribe() { + source.unsubscribe(handleSubscriptionChange); + }; + }, +}); + +// Rather than passing the subscribable source to our ExampleComponent, +// We could just pass the subscribed value directly: +// highlight-range{1-3} + + {value => } +; From 65eca09b5099d787255d8f69b5af86e184dc432c Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sat, 24 Mar 2018 21:44:22 -0700 Subject: [PATCH 55/61] Tweaks --- content/blog/2018-03-15-update-on-async-rendering.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/content/blog/2018-03-15-update-on-async-rendering.md b/content/blog/2018-03-15-update-on-async-rendering.md index e2fe9cf321d..74c3f87a791 100644 --- a/content/blog/2018-03-15-update-on-async-rendering.md +++ b/content/blog/2018-03-15-update-on-async-rendering.md @@ -102,10 +102,6 @@ People often assume that `componentWillMount` and `componentWillUnmount` are alw For this reason, the recommended way to add listeners/subscriptions is to use the `componentDidMount` lifecycle: `embed:update-on-async-rendering/adding-event-listeners-after.js` -> Note -> -> Although the pattern above is slightly more verbose, it has an added benefit of deferring the subscription creation until after the component has rendered, reducing the amount of time in the critical render path. In the near future, React may include more tools to reduce code complexity for data fetching cases like this. - 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`](https://github.com/facebook/react/tree/master/packages/create-subscription), to help with this. 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: @@ -121,14 +117,14 @@ Rather than passing a subscribable `dataSource` prop as we did in the example ab Here is an example of a component that uses the legacy `componentWillReceiveProps` lifecycle to update `state` based on new `props` values: `embed:update-on-async-rendering/updating-state-from-props-before.js` -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 has been deprecated. +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.): `embed:update-on-async-rendering/updating-state-from-props-after.js` > Note > -> The [`react-lifecycles-compat`](https://github.com/reactjs/react-lifecycles-compat) polyfill enables this new lifecycle to be used with older versions of React as well. [Learn more about how to use it below.](http://localhost:8000/blog/2018/02/07/update-on-async-rendering.html#open-source-project-maintainers) +> If you're writing a shared component, the [`react-lifecycles-compat`](https://github.com/reactjs/react-lifecycles-compat) polyfill enables the new lifecycle to be used with older versions of React as well. [Learn more about how to use it below.](#open-source-project-maintainers) ### Invoking external callbacks From 7ced9ce78b7a6a1d7986d9396618fca7107feaa4 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 27 Mar 2018 10:12:55 -0700 Subject: [PATCH 56/61] Renamed blog post --- ...async-rendering.md => 2018-03-27-update-on-async-rendering.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename content/blog/{2018-03-15-update-on-async-rendering.md => 2018-03-27-update-on-async-rendering.md} (100%) diff --git a/content/blog/2018-03-15-update-on-async-rendering.md b/content/blog/2018-03-27-update-on-async-rendering.md similarity index 100% rename from content/blog/2018-03-15-update-on-async-rendering.md rename to content/blog/2018-03-27-update-on-async-rendering.md From 7cf5b581873a1359e807dff9cda2394a973a08d4 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 27 Mar 2018 10:30:38 -0700 Subject: [PATCH 57/61] Wordsmithing --- .../blog/2018-03-27-update-on-async-rendering.md | 6 +++--- ...adding-event-listeners-create-subscription.js | 16 ++++++++-------- .../updating-state-from-props-after.js | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/content/blog/2018-03-27-update-on-async-rendering.md b/content/blog/2018-03-27-update-on-async-rendering.md index 74c3f87a791..e6aaf7a1931 100644 --- a/content/blog/2018-03-27-update-on-async-rendering.md +++ b/content/blog/2018-03-27-update-on-async-rendering.md @@ -23,7 +23,7 @@ These lifecycle methods have often been misunderstood and subtly misused; furthe **Note that if you're an 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.** -At Facebook, we maintain over 50,000 React components, so we're in the same boat as you. We can't rewrite our apps so we will take the gradual migration path together with everyone in the React community. +We maintain over 50,000 React components at Facebook, so we understand that migrations take time. We will take the gradual migration path together with everyone in the React community. --- @@ -88,7 +88,7 @@ There is a common misconception that fetching in `componentWillMount` lets you a > > 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](https://gist.github.com/bvaughn/89700e525ff423a75ffb63b1b1e30a8f). > -> 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](/blog/2018/03/01/sneak-peek-beyond-react-16.html). 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 at the moment it is not quite ready yet. +> 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](/blog/2018/03/01/sneak-peek-beyond-react-16.html). 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. ### Adding event listeners (or subscriptions) @@ -124,7 +124,7 @@ As of version 16.3, the recommended way to update `state` in response to `props` > Note > -> If you're writing a shared component, the [`react-lifecycles-compat`](https://github.com/reactjs/react-lifecycles-compat) polyfill enables the new lifecycle to be used with older versions of React as well. [Learn more about how to use it below.](#open-source-project-maintainers) +> If you're writing a shared component, the [`react-lifecycles-compat`](https://github.com/reactjs/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.](#open-source-project-maintainers) ### Invoking external callbacks diff --git a/examples/update-on-async-rendering/adding-event-listeners-create-subscription.js b/examples/update-on-async-rendering/adding-event-listeners-create-subscription.js index 9b096070a67..fd03d0c3f74 100644 --- a/examples/update-on-async-rendering/adding-event-listeners-create-subscription.js +++ b/examples/update-on-async-rendering/adding-event-listeners-create-subscription.js @@ -1,26 +1,26 @@ import {createSubscription} from 'create-subscription'; const Subscription = createSubscription({ - getCurrentValue(source) { - // Return the current value of the subscription (source). + getCurrentValue(sourceProp) { + // Return the current value of the subscription (sourceProp). // highlight-next-line - return source.value; + return sourceProp.value; }, - subscribe(source, callback) { + subscribe(sourceProp, callback) { function handleSubscriptionChange() { - callback(dataSource.value); + callback(sourceProp.value); } - // Subscribe (e.g. add an event listener) to the subscription (source). + // Subscribe (e.g. add an event listener) to the subscription (sourceProp). // Call callback(newValue) whenever a subscription changes. // highlight-next-line - source.subscribe(handleSubscriptionChange); + sourceProp.subscribe(handleSubscriptionChange); // Return an unsubscribe method. // highlight-range{1-3} return function unsubscribe() { - source.unsubscribe(handleSubscriptionChange); + sourceProp.unsubscribe(handleSubscriptionChange); }; }, }); diff --git a/examples/update-on-async-rendering/updating-state-from-props-after.js b/examples/update-on-async-rendering/updating-state-from-props-after.js index fe5de41165d..19b920a9969 100644 --- a/examples/update-on-async-rendering/updating-state-from-props-after.js +++ b/examples/update-on-async-rendering/updating-state-from-props-after.js @@ -7,7 +7,7 @@ class ExampleComponent extends React.Component { isScrollingDown: false, lastRow: null, }; - // highlight-line + // highlight-range{1-8} static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.currentRow !== prevState.lastRow) { From 9f7240337f0abcefc9c94a81d1420e46d5e9a06a Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 27 Mar 2018 12:51:00 -0700 Subject: [PATCH 58/61] Updated gradual migration note --- content/blog/2018-03-27-update-on-async-rendering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2018-03-27-update-on-async-rendering.md b/content/blog/2018-03-27-update-on-async-rendering.md index e6aaf7a1931..0b9755852de 100644 --- a/content/blog/2018-03-27-update-on-async-rendering.md +++ b/content/blog/2018-03-27-update-on-async-rendering.md @@ -23,7 +23,7 @@ These lifecycle methods have often been misunderstood and subtly misused; furthe **Note that if you're an 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, so we understand that migrations take time. We will take the gradual migration path together with everyone in the React community. +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. --- From 712f4deb8525d6b387978f2fec6ed5605dc164d3 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 27 Mar 2018 12:51:21 -0700 Subject: [PATCH 59/61] Changed wording about app developer to React app developer --- content/blog/2018-03-27-update-on-async-rendering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2018-03-27-update-on-async-rendering.md b/content/blog/2018-03-27-update-on-async-rendering.md index 0b9755852de..1cbee8e85cf 100644 --- a/content/blog/2018-03-27-update-on-async-rendering.md +++ b/content/blog/2018-03-27-update-on-async-rendering.md @@ -21,7 +21,7 @@ These lifecycle methods have often been misunderstood and subtly misused; furthe * **16.x**: 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 an 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.** +**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. From 46103921fe76cd5d2e6e517eff2ee8461985083f Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 27 Mar 2018 14:06:27 -0700 Subject: [PATCH 60/61] Changes in response to Sophie's feedback --- .../2018-03-27-update-on-async-rendering.md | 18 +++++++++--------- ...ding-event-listeners-create-subscription.js | 4 ---- ...react-dom-properties-before-update-after.js | 1 + ...eact-dom-properties-before-update-before.js | 15 ++++++++------- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/content/blog/2018-03-27-update-on-async-rendering.md b/content/blog/2018-03-27-update-on-async-rendering.md index 1cbee8e85cf..911fecd21b9 100644 --- a/content/blog/2018-03-27-update-on-async-rendering.md +++ b/content/blog/2018-03-27-update-on-async-rendering.md @@ -11,14 +11,14 @@ One of the biggest lessons we've learned is that some of our legacy component li * `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. +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](/blog/2016/02/19/new-versioning-scheme.html), 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.) -* **16.x**: 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.) +* **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.** @@ -45,16 +45,12 @@ Together with `componentDidUpdate`, this new lifecycle should cover all use case ### New lifecycle: `getSnapshotBeforeUpdate` -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`. +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`. We'll look at examples of how both of these lifecycles can be used below. -> Note -> -> For brevity, the examples below are written using the experimental class properties transform, but the same migration strategies apply without it. - ## Examples - [Initializing state](#initializing-state) - [Fetching external data](#fetching-external-data) @@ -64,6 +60,10 @@ We'll look at examples of how both of these lifecycles can be used below. - [Updating external data when props change](#updating-external-data-when-props-change) - [Reading DOM properties before an update](#reading-dom-properties-before-an-update) +> 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`: @@ -102,7 +102,7 @@ People often assume that `componentWillMount` and `componentWillUnmount` are alw For this reason, the recommended way to add listeners/subscriptions is to use the `componentDidMount` lifecycle: `embed:update-on-async-rendering/adding-event-listeners-after.js` -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`](https://github.com/facebook/react/tree/master/packages/create-subscription), to help with this. +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`](https://github.com/facebook/react/tree/master/packages/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: @@ -171,7 +171,7 @@ Open source maintainers might be wondering what these changes mean for shared co Fortunately, you do not! -In support of version 16.3, we've also created a new NPM package, [`react-lifecycles-compat`](https://github.com/reactjs/react-lifecycles-compat). This package polyfills components so that the new `getDerivedStateFromProps` lifecycle will also work with older versions of React (0.14.9+). +When React 16.3 is published, we'll also publish a new npm package, [`react-lifecycles-compat`](https://github.com/reactjs/react-lifecycles-compat). This package polyfills components so that the new `getDerivedStateFromProps` lifecycle will also work with older versions of React (0.14.9+). To use this polyfill, first add it as a dependency to your library: diff --git a/examples/update-on-async-rendering/adding-event-listeners-create-subscription.js b/examples/update-on-async-rendering/adding-event-listeners-create-subscription.js index fd03d0c3f74..d636db4993a 100644 --- a/examples/update-on-async-rendering/adding-event-listeners-create-subscription.js +++ b/examples/update-on-async-rendering/adding-event-listeners-create-subscription.js @@ -3,7 +3,6 @@ import {createSubscription} from 'create-subscription'; const Subscription = createSubscription({ getCurrentValue(sourceProp) { // Return the current value of the subscription (sourceProp). - // highlight-next-line return sourceProp.value; }, @@ -14,11 +13,9 @@ const Subscription = createSubscription({ // Subscribe (e.g. add an event listener) to the subscription (sourceProp). // Call callback(newValue) whenever a subscription changes. - // highlight-next-line sourceProp.subscribe(handleSubscriptionChange); // Return an unsubscribe method. - // highlight-range{1-3} return function unsubscribe() { sourceProp.unsubscribe(handleSubscriptionChange); }; @@ -27,7 +24,6 @@ const Subscription = createSubscription({ // Rather than passing the subscribable source to our ExampleComponent, // We could just pass the subscribed value directly: -// highlight-range{1-3} {value => } ; diff --git a/examples/update-on-async-rendering/react-dom-properties-before-update-after.js b/examples/update-on-async-rendering/react-dom-properties-before-update-after.js index 7f2ead716a1..2fc188aa385 100644 --- a/examples/update-on-async-rendering/react-dom-properties-before-update-after.js +++ b/examples/update-on-async-rendering/react-dom-properties-before-update-after.js @@ -15,6 +15,7 @@ class ScrollingList extends React.Component { 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; diff --git a/examples/update-on-async-rendering/react-dom-properties-before-update-before.js b/examples/update-on-async-rendering/react-dom-properties-before-update-before.js index a14fcc6b1aa..ad26b4e818d 100644 --- a/examples/update-on-async-rendering/react-dom-properties-before-update-before.js +++ b/examples/update-on-async-rendering/react-dom-properties-before-update-before.js @@ -1,24 +1,25 @@ class ScrollingList extends React.Component { listRef = null; - prevScrollHeight = null; + previousScrollHeight = null; // highlight-range{1-7} 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.prevScrollHeight = this.listRef.scrollHeight; + this.previousScrollHeight = this.listRef.scrollHeight; } } - // highlight-range{1-9} + // highlight-range{1-10} componentDidUpdate(prevProps, prevState) { - // If prevScrollHeight is set, we've just added new items. + // 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.prevScrollHeight !== null) { + if (this.previousScrollHeight !== null) { this.listRef.scrollTop += - this.listRef.scrollHeight - this.prevScrollHeight; - this.prevScrollHeight = null; + this.listRef.scrollHeight - + this.previousScrollHeight; + this.previousScrollHeight = null; } } From b824bd22bc4894af9bca061c6dbf00c319ccbd7f Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 27 Mar 2018 14:25:29 -0700 Subject: [PATCH 61/61] Added getSnapshotBeforeUpdate to the polyfill notes --- content/blog/2018-03-27-update-on-async-rendering.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/content/blog/2018-03-27-update-on-async-rendering.md b/content/blog/2018-03-27-update-on-async-rendering.md index 911fecd21b9..1a3bf5f5255 100644 --- a/content/blog/2018-03-27-update-on-async-rendering.md +++ b/content/blog/2018-03-27-update-on-async-rendering.md @@ -161,6 +161,10 @@ The two lifecycles can be used together like this: `embed:update-on-async-rendering/react-dom-properties-before-update-after.js` +> Note +> +> If you're writing a shared component, the [`react-lifecycles-compat`](https://github.com/reactjs/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.](#open-source-project-maintainers) + ## 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](https://github.com/reactjs/reactjs.org/issues/new) 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. @@ -171,7 +175,7 @@ Open source maintainers might be wondering what these changes mean for shared co Fortunately, you do not! -When React 16.3 is published, we'll also publish a new npm package, [`react-lifecycles-compat`](https://github.com/reactjs/react-lifecycles-compat). This package polyfills components so that the new `getDerivedStateFromProps` lifecycle will also work with older versions of React (0.14.9+). +When React 16.3 is published, we'll also publish a new npm package, [`react-lifecycles-compat`](https://github.com/reactjs/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: @@ -183,7 +187,7 @@ yarn add react-lifecycles-compat npm install react-lifecycles-compat --save ``` -Next, update your components to use the new static lifecycle, `getDerivedStateFromProps`, as described above. +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: `embed:update-on-async-rendering/using-react-lifecycles-compat.js` \ No newline at end of file