Skip to content

Commit 41ade82

Browse files
committed
Wording improvements in response to Dan's feedback
1 parent 8bc2a14 commit 41ade82

7 files changed

+37
-37
lines changed

content/blog/2018-02-07-update-on-async-rendering.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,23 @@ Along the way our research has shown that some of our legacy component lifecycle
1111
* `componentWillReceiveProps`
1212
* `componentWillUpdate`
1313

14-
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:
14+
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:
1515

1616
* **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.)
1717
* **16.4**: Enable deprecation warning for `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate`. (Both the old lifecycle names and the new aliases will work in this release.)
1818
* **17.0**: Remove `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate` . (Only the new "UNSAFE_" lifecycle names will work in this release.)
1919

20-
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.
20+
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.
2121

22-
## What can Asynchronous Rendering do?
22+
## What can asynchronous rendering do?
2323

2424
#### With every new version, our goal is to improve the user experience of apps created with React.
2525

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

2828
We found that asynchronous rendering can help in several ways. For example:
2929

30-
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.
30+
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.
3131
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.
3232
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.)
3333
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
4141

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

44-
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.
44+
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.
4545

4646
### Initializing state
4747

@@ -96,9 +96,15 @@ As of version 16.3, the recommended way to update `state` in response to `props`
9696
Here is an example of a component that calls an external function when its internal state changes:
9797
`embed:update-on-async-rendering/invoking-external-callbacks-before.js`
9898

99-
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:
99+
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).
100+
101+
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:
100102
`embed:update-on-async-rendering/invoking-external-callbacks-after.js`
101103

104+
## Other scenarios
105+
106+
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.
107+
102108
## Open source project maintainers
103109

104110
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?
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
// After
22
class ExampleComponent extends React.Component {
3-
// highlight-range{1-6}
3+
// highlight-range{1-4}
44
state = {
5-
count: 0,
6-
derivedValue: computeDerivedValue(
7-
this.props
8-
),
5+
isScrollingDown: false,
6+
lastRow: this.props.currentRow,
97
};
108
}
Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
// Before
22
class ExampleComponent extends React.Component {
3-
// highlight-next-line
43
state = {};
54

6-
// highlight-range{1-8}
5+
// highlight-range{1-6}
76
componentWillMount() {
87
this.setState({
9-
count: 0,
10-
derivedValue: computeDerivedValue(
11-
this.props
12-
),
8+
isScrollingDown: false,
9+
lastRow: this.props.currentRow,
1310
});
1411
}
1512
}

examples/update-on-async-rendering/invoking-external-callbacks-after.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
// After
22
class ExampleComponent extends React.Component {
3-
// highlight-range{1-13}
3+
// highlight-next-line
44
componentDidUpdate(
55
prevProps,
66
prevState
77
) {
8+
// highlight-range{1-8}
89
if (
910
this.state.someStatefulValue !==
1011
prevState.someStatefulValue

examples/update-on-async-rendering/invoking-external-callbacks-before.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
// Before
22
class ExampleComponent extends React.Component {
3-
// highlight-range{1-13}
3+
// highlight-next-line
44
componentWillUpdate(
55
nextProps,
66
nextState
77
) {
8+
// highlight-range{1-8}
89
if (
910
this.state.someStatefulValue !==
1011
nextState.someStatefulValue

examples/update-on-async-rendering/updating-state-from-props-after.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
// After
22
class ExampleComponent extends React.Component {
3-
// highlight-range{1-3}
43
// Initialize state in constructor,
54
// Or with a property initializer.
5+
// highlight-next-line
66
state = {};
77

8-
// highlight-range{1-20}
8+
// highlight-next-line
99
static getDerivedStateFromProps(
1010
nextProps,
1111
prevState
1212
) {
13+
// highlight-range{1-11}
1314
if (
14-
prevState.someMirroredValue !==
15-
nextProps.someValue
15+
nextProps.currentRow !==
16+
prevState.lastRow
1617
) {
1718
return {
18-
derivedData: computeDerivedState(
19-
nextProps
20-
),
21-
someMirroredValue:
22-
nextProps.someValue,
19+
lastRow: nextProps.currentRow,
20+
isScrollingDown:
21+
nextProps.currentRow >
22+
prevState.lastRow,
2323
};
2424
}
2525

examples/update-on-async-rendering/updating-state-from-props-before.js

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
11
// Before
22
class ExampleComponent extends React.Component {
3-
// highlight-range{1-5}
43
state = {
5-
derivedData: computeDerivedState(
6-
this.props
7-
),
4+
isScrollingDown: false,
85
};
96

107
// highlight-range{1-12}
118
componentWillReceiveProps(nextProps) {
129
if (
13-
this.props.someValue !==
14-
nextProps.someValue
10+
this.props.currentRow !==
11+
nextProps.currentRow
1512
) {
1613
this.setState({
17-
derivedData: computeDerivedState(
18-
nextProps
19-
),
14+
isScrollingDown:
15+
nextProps.currentRow >
16+
this.props.currentRow,
2017
});
2118
}
2219
}

0 commit comments

Comments
 (0)