Skip to content

An update on async rendering #596

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 64 commits into from
Mar 27, 2018
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
544c0bc
Migrated async update from Quip to Markdown
bvaughn Feb 6, 2018
e298b47
Wordsmithing
bvaughn Feb 6, 2018
9ff0f12
Split StrictMode into its own top level docs page
bvaughn Feb 6, 2018
0674c34
Added string ref section to strict mode page
bvaughn Feb 6, 2018
cdb4b9e
Added TOC header links to Strict Mode page
bvaughn Feb 6, 2018
289a2da
Prettier
bvaughn Feb 6, 2018
99fedea
Wording changes in response to PR feedback
bvaughn Feb 6, 2018
49464f7
Removed some unnecessary parens
bvaughn Feb 6, 2018
fe6b133
Add Dan as an author to "Update on Async"
bvaughn Feb 6, 2018
1314117
Wording improvements in response to Dan's feedback
bvaughn Feb 7, 2018
f005c04
Wordsmithing
bvaughn Feb 7, 2018
06d5be4
Prettier
bvaughn Feb 7, 2018
3696388
Increased Prettier line-width for examples
bvaughn Feb 7, 2018
ac23b1f
Trigger Netlify rebuild
bvaughn Feb 7, 2018
5cae7c6
Strict Mode blog title capitalization
bvaughn Feb 7, 2018
f70c0dd
Minor wordsmithing
bvaughn Feb 7, 2018
c1e67be
Wordsmithing in response to PR feedback
bvaughn Feb 12, 2018
75a43aa
Wordsmithing in response to PR feedback
bvaughn Feb 12, 2018
fb3b91f
Add explanation for render and commit lifecycles
bvaughn Feb 12, 2018
60d65ce
Wordsmithing
bvaughn Feb 12, 2018
f632f22
Wording changes for update-on-async and strict-mode
bvaughn Feb 13, 2018
626ac42
Moved StrictMode to docs rather than blog post
bvaughn Feb 13, 2018
7456327
Combined notes about react-lifecycles-compat
bvaughn Feb 13, 2018
2909738
Address more Sophie feedback
bvaughn Feb 13, 2018
5400338
Updated data-fetching example to show cWRP too
bvaughn Feb 13, 2018
813be17
Updated docs/recipes in response to a new GH question
bvaughn Feb 14, 2018
8de7dc4
Updated recipes to show updating/removing subscriptions
bvaughn Feb 16, 2018
c45fb40
Triggering rebuild of Netlify
bvaughn Feb 21, 2018
858c1a7
Fixed small error in example
bvaughn Feb 21, 2018
98d5a09
Added gDSFP to example
bvaughn Feb 21, 2018
442591c
Added example of updating subscription from props
bvaughn Feb 22, 2018
b1ce572
Typo
bvaughn Feb 23, 2018
2312173
Moved updating-subscription example into an external Gist with a note
bvaughn Feb 28, 2018
16eb646
Fixed typo
bvaughn Feb 28, 2018
4d16523
Typo
bvaughn Feb 28, 2018
9905159
Merge branch 'master' into update-on-async-rendering
bvaughn Mar 7, 2018
1ca6cfc
Moved async update blog post to a later, random date
bvaughn Mar 7, 2018
7408e07
Remoaved 'What can asynchronous rendering do?' section and instead li…
bvaughn Mar 7, 2018
3c75def
Deleted StrictMode and examples/images
bvaughn Mar 7, 2018
55650fc
Added explicit null value in state initializer
bvaughn Mar 15, 2018
97a109d
Added a note about experimental class properties usage in examples
bvaughn Mar 15, 2018
21fa116
Removed StrictMode from side nav
bvaughn Mar 16, 2018
92cf72d
Hardened wording a bit around async
bvaughn Mar 20, 2018
fa34fcf
16.4 -> 16.x
bvaughn Mar 22, 2018
b3bf0bd
Added getSnapshotBeforeUpdate recipe
bvaughn Mar 23, 2018
254fc8b
Wordsmithing nits
bvaughn Mar 23, 2018
b0c22f7
Wording tweak
bvaughn Mar 23, 2018
558d576
Reworked introduction
bvaughn Mar 24, 2018
7425aed
Typofix
bvaughn Mar 24, 2018
65b1496
Typo
bvaughn Mar 24, 2018
6eae811
Tweaks to async post
gaearon Mar 24, 2018
a2139de
Add a note about suspense
gaearon Mar 24, 2018
030980e
Merge pull request #6 from gaearon/tweaks-async-post
bvaughn Mar 24, 2018
e110ac5
Added a small TOC for examples
bvaughn Mar 24, 2018
ce060eb
Imported theme style tweaks from PR 587
bvaughn Mar 24, 2018
e143823
Added create-subscription example
bvaughn Mar 24, 2018
65eca09
Tweaks
bvaughn Mar 25, 2018
a3ea63a
Merge branch 'master' into update-on-async-rendering
bvaughn Mar 27, 2018
7ced9ce
Renamed blog post
bvaughn Mar 27, 2018
7cf5b58
Wordsmithing
bvaughn Mar 27, 2018
9f72403
Updated gradual migration note
bvaughn Mar 27, 2018
712f4de
Changed wording about app developer to React app developer
bvaughn Mar 27, 2018
4610392
Changes in response to Sophie's feedback
bvaughn Mar 27, 2018
b824bd2
Added getSnapshotBeforeUpdate to the polyfill notes
bvaughn Mar 27, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 173 additions & 0 deletions content/blog/2018-02-07-update-on-async-rendering.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
---
title: Update on Async Rendering
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a sentence akin to your later "Asynchronous rendering solves this by splitting rendering into small chunks that can be paused and later resumed." up here? Not everyone will click through but most people will appreciate a quick refresher.

Or you could put it in the "We found that asynchronous rendering can help in several ways." paragraph before the list.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I quite follow what you're asking with this comment.


Along the way, our research has shown that some of our 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:

* **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.)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any vague sense of timing we could give here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm reluctant to make any predictions on timing.

I could say "within a week or two", "within a month or two", etc. if that would be helpful?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eh, I guess not. I think the most valuable would be to say 17 is at least X months away but honestly I have no idea


Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any plans to have a codemod to auto-prefix with UNSAFE?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there is rename-unsafe-lifecycles, but it is intended for updating application code and so will probably not be mentioned until the deprecation warnings have been turned on with 16.4.

The 16.3 release is intended to give library authors a head start on fixing async things in advance of the deprecation warnings being turned on. Unfortunately, just renaming the methods won't actually fix any of the potential async problems, so I don't think mentioning the codemod is a good idea for 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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe "on the deprecated lifecycles" -> "on these legacy lifecycles". Since they're not technically deprecated yet. Also too many people read "deprecated" as "immediately removed" so I prefer other words when possible.

## 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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe: insert a paragraph about how React batches/coalesces updates already? I've always found that people are receptive to batching and in some sense, async rendering is an extension of that.

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.
Copy link
Member

@gaearon gaearon Feb 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe:

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.

As we were working on these features, we discovered that three particular lifecycle methods are too coupled to the implementation details of the past versions of React. By replacing those problematic lifecycles with safer alternatives, we 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is a standalone post (my preference), we will need a section hereabouts summarizing the changes. I would like us to call out that the intention of the change is to make the lifecycle methods more functional and pure. And also say why instance variables are problematic (it's sorta implicit in your post but I'd love to have a sentence people can point to for this saying it's bad).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We kind of summarized most of the relevant changes in the intro to this (standalone) post. I think the only thing we haven't explicitly mentioned yet is the new static lifecycle.

I'll try to introduce this. I'm not sure on wording yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a section, "Before we begin, here’s a quick reminder of the lifecyle changes in version 16.3". Hopefully this is something like what yo had in mind.


#### 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 avoids the problematic lifecycles.

---

Before we begin, here's a quick reminder of the lifecyle changes in version 16.3:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: "lifecyle" -> "lifecycle"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can only spell at a 3rd grade level

* 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`:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guessing you mean introducing not introducinc?
Not sure if you want random people's comments on this, but I appreciate being able to see it! Really love the example oriented nature of this writeup

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops. Thanks for pointing that out.


```js
static getDerivedStateFromProps(
nextProps: Props,
prevState: State
): $Shape<State> | 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think people will read this as examples of things that are problematic with async mode? I would have a concern that people will interpret them that way and then get confused because (for example) this example would actually be fine in async mode.

Maybe frame this section not as "examples that are async-safe" but rather just "examples that avoid the problematic methods"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I can see that possible interpretation.

I didn't want to write examples that felt intentionally problematic, because I didn't want people to react by thinking, "Well just don't make that obvious mistake and you're fine".

The goal of this section is to show people how to replace existing patterns that depend on the old lifecycles with similar patterns that fit within the suggested lifecycles (and also happen to be safer for async and SSR).


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 state initialization to the constructor or to a property initializer, like so:
`embed:update-on-async-rendering/initializing-state-after.js`

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about adding

Note:

Here and below, we're using the short property initializer syntax proposed for a future version of JavaScript. It is available by default in Create React App and any projects using Babel class properties transform.

It is equivalent to defining a class property in the constructor:

constructor(props) {
 super(props);
 this.state = {
   count: 0,
   // ...
 };
}

### 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 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`

Copy link
Member

@gaearon gaearon Feb 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add:

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IME the number-one reason people do this is because they have cWM() { FooStore.subscribe(() => this.setState()) } where that calls immediately with the current value if it is already loaded. But I think what you have here is still good.

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).

### 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 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).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this and the lifecycle method is being deprecated 😝

might be worth calling out that there's more symmetry between didMount and willUnmount both taking place while the component is mounted and refs and dom apis are available.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I like this way of framing it. I'm a little unsure of the wording, but I'll take a stab at it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice hammering of the point that most of these patterns are already SSR-unsafe 👍


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`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After this example, can we acknowledge that this is a little clunky (since it is definitely more code even without the proper cWRP here) and we'll try to improve it later?

"Although this is slightly more code, this pattern means that the subscription creation can be deferred until after the component renders on screen, reducing the amount of time in the critical render path. In the future, React may include more tools to manage data fetching efficiently and reduce code complexity."

something like that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great. I had the same concern.


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

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

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.

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`

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add one sentence reminding folks when getDerivedStateFromProps is called here? (In particular, emphasize whether it happens on initial mount and maybe say it's pure.)

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

### 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`

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).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are setState calls being flushed to the UI before the user sees it a new feature? Assumed in current React that didMount meant it's in the DOM and it's visible, same with didUpdate

Copy link
Contributor Author

@bvaughn bvaughn Feb 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. This has always been the case.

Libraries like react-virtualized depend on this behavior, for example, by measuring DOM elements in componentDidMount/componentDidUpdate and potentially re-rendering. The additional render is processed synchronously, so the user doesn't see the transient UI state.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jeez, that is news to me. I've got to write about this now. 100% my assumption has always been "mounted/update" = "visible to user".
Glad that's cleared up. I assume I'm not alone in this assumption.

Copy link
Contributor Author

@bvaughn bvaughn Feb 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it is "visible" in that it's in the DOM, but the sync update is processed immediately so the user doesn't actually have time to see it. (And any external code that's waiting on the render/update to complete won't finish execution until after the sync-render-update has completed.)

RV components like AutoSizer and CellMeasurer depend on this, as do components that do things like position tooltips.

Edit for people who may stumble across this thread in the future: Andrew's comment adds clarity: https://twitter.com/acdlite/status/968938221852753921


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`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is calling props.onChange during render even a supported pattern? I guess it can be but it feels dicey and strikes me as not-well-thought-out data flow. (esp. if it leads to another rerender) Maybe we can replace this with something that is clearly not side effectful within the application. Performance logging came to mind but I guess that is the one case where you would want componentWillUpdate. Would really love to find a better example here before publishing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found places within Facebook code where we were doing this. That made me want to specifically call it out as a pattern to avoid.


Copy link
Member

@gaearon gaearon Feb 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add:

Sometimes people use componentWillUpdate out of a misplaced fear that by the time componentDidUpdate fires, it is "too late" to update 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).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this clarification.

Side note: I wonder how async will impact this. I think it will be important to at least have the option of doing a sync setState flush (for components like RV that require the ability to measure the DOM before doing a meaningful initial render).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the idea is that even in async, setState during commit is sync? At least that's how it works now AFAIK.

### 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.

## Open source project maintainers
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add a section before this one. Something like

Other scenarios

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love it.


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!

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:

```bash
# Yarn
yarn add react-lifecycles-compat

# NPM
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`
2 changes: 2 additions & 0 deletions content/docs/nav.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@
title: Accessibility
- id: code-splitting
title: Code-Splitting
- id: strict-mode
title: StrictMode
- title: Reference
items:
- id: react-api
Expand Down
Loading