-
Notifications
You must be signed in to change notification settings - Fork 46.9k
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
Allow useReducer to bail out of rendering by returning previous state #14569
Conversation
ReactDOM: size: 🔺+0.4%, gzip: 🔺+0.4% Details of bundled changes.Comparing: 8a12009...6c9ae30 react-dom
react-art
react-native-renderer
react-test-renderer
react-reconciler
Generated by 🚫 dangerJS |
f3804aa
to
6a1c6f8
Compare
d076142
to
695f36f
Compare
packages/react-reconciler/src/__tests__/ReactNewContext-test.internal.js
Outdated
Show resolved
Hide resolved
695f36f
to
4c65d54
Compare
We should also change the SSR, no? For example function Foo(props) {
const [name, setName] = useState('Dan');
if (props.name === 'Andrew') {
setName(props.name);
}
return name;
}
renderToString(<Foo name="Andrew" /> would previously go into an infinite loop but I think should now stabilize. |
@gaearon The eager bailout doesn't apply to render phase updates, but that's an interesting idea. |
I think we should only eagerly compute render phase updates and bail out if we're sure the bailout can be relied on completely and predictably. Not sure about that. Seems safer to always require the guard around the update. |
The guard provides a natural place to other code that doesn't need to be evaluated if nothing has changed so it seems good to encourage that. |
Although, counter-argument: This optimization isn’t really meant for useState. It’s mostly for useReducer where it is difficult to know if a reducer will yield the same value or not. |
This is conceptually similar to `shouldComponentUpdate`, except because there could be multiple useReducer (or useState) Hooks in a single component, we can only bail out if none of the Hooks produce a new value. We also can't bail out if any the other types of inputs — state and context — have changed. These optimizations rely on the constraint that components are pure functions of props, state, and context. In some cases, we can bail out without entering the render phase by eagerly computing the next state and comparing it to the current one. This only works if we are absolutely certain that the queue is empty at the time of the update. In concurrent mode, this is difficult to determine, because there could be multiple copies of the queue and we don't know which one is current without doing lots of extra work, which would defeat the purpose of the optimization. However, in our implementation, there are at most only two copies of the queue, and if *both* are empty then we know that the current queue must be.
Should not bail out during subsequent update. (This isn't directly related to this PR because we should have had this test, anyway.)
15ca89d
to
e7a99f9
Compare
f178758
to
6c9ae30
Compare
This is conceptually similar to
shouldComponentUpdate
, except because there could be multipleuseReducer
(oruseState
) Hooks in a single component, we can only bail out if none of the Hooks produce a new value. We also can't bail out if any the other types of inputs — props and context — have changed.These optimizations rely on the constraint that components are pure functions of props, state, and context.
In some cases, we can bail out without entering the render phase by eagerly computing the next state and comparing it to the current one. This only works if we are absolutely certain that the queue is empty at the time of the update. In concurrent mode, this is difficult to determine, because there could be multiple copies of the queue and we don't know which one is current without doing lots of extra work, which would defeat the purpose of the optimization. However, in our implementation, there are at most only two copies of the queue, and if both are empty then we know that the current queue must be.
Summary of changes:
PerformedWork
effect tag bit to track whether a component's props, state, or context has changed. This means we need to mark it dirty every time we detect that one of those inputs has a new value. Props and state are fairly straightforward, but context is tricky. I needed to store something on the fiber that represents whether it contains a pending context update. I didn't want to add an additional fiber field, because most fibers do not read from context. So I changed the existingfirstContextDependency
field tocontextDependencies
. Instead of pointing to the first item in the list, it points to a list object contains bothfirst
andexpirationTime
.