-
Notifications
You must be signed in to change notification settings - Fork 46.4k
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
Fixed a batched-state update bug with getDerivedStateFromProps #12408
Changes from 1 commit
876eacc
6e4f17e
1cd00a3
88b41c1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -448,6 +448,7 @@ export default function( | |
workInProgress, | ||
instance, | ||
props, | ||
state, | ||
); | ||
|
||
if (partialState !== null && partialState !== undefined) { | ||
|
@@ -534,7 +535,8 @@ export default function( | |
function callGetDerivedStateFromProps( | ||
workInProgress: Fiber, | ||
instance: any, | ||
props: any, | ||
nextProps: any, | ||
prevState: any, | ||
) { | ||
const {type} = workInProgress; | ||
|
||
|
@@ -567,17 +569,13 @@ export default function( | |
workInProgress.mode & StrictMode) | ||
) { | ||
// Invoke method an extra time to help detect side-effects. | ||
type.getDerivedStateFromProps.call( | ||
null, | ||
props, | ||
workInProgress.memoizedState, | ||
); | ||
type.getDerivedStateFromProps.call(null, nextProps, prevState); | ||
} | ||
|
||
const partialState = type.getDerivedStateFromProps.call( | ||
null, | ||
props, | ||
workInProgress.memoizedState, | ||
nextProps, | ||
prevState, | ||
); | ||
|
||
if (__DEV__) { | ||
|
@@ -698,19 +696,21 @@ export default function( | |
} | ||
} | ||
|
||
// Compute the next state using the memoized state and the update queue. | ||
const oldState = workInProgress.memoizedState; | ||
// TODO: Previous state can be null. | ||
let newState; | ||
|
||
let derivedStateFromProps; | ||
if (oldProps !== newProps) { | ||
derivedStateFromProps = callGetDerivedStateFromProps( | ||
workInProgress, | ||
instance, | ||
newProps, | ||
oldState, | ||
); | ||
} | ||
|
||
// Compute the next state using the memoized state and the update queue. | ||
const oldState = workInProgress.memoizedState; | ||
// TODO: Previous state can be null. | ||
let newState; | ||
let derivedStateFromCatch; | ||
if (workInProgress.updateQueue !== null) { | ||
newState = processUpdateQueue( | ||
|
@@ -867,19 +867,11 @@ export default function( | |
} | ||
} | ||
|
||
let derivedStateFromProps; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why did you change the order of It looks like the order in the "resume mount" path is different now, though. Should be consistent regardless. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This was incidental. I changed the order of
Fair 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One more thing: could you add a test so the order doesn't incidentally change in the future? |
||
if (oldProps !== newProps) { | ||
derivedStateFromProps = callGetDerivedStateFromProps( | ||
workInProgress, | ||
instance, | ||
newProps, | ||
); | ||
} | ||
|
||
// Compute the next state using the memoized state and the update queue. | ||
const oldState = workInProgress.memoizedState; | ||
// TODO: Previous state can be null. | ||
let newState; | ||
let newState = oldState; | ||
|
||
let derivedStateFromCatch; | ||
if (workInProgress.updateQueue !== null) { | ||
newState = processUpdateQueue( | ||
|
@@ -908,27 +900,38 @@ export default function( | |
capturedValues, | ||
); | ||
} | ||
} else { | ||
newState = oldState; | ||
} | ||
|
||
if (derivedStateFromProps !== null && derivedStateFromProps !== undefined) { | ||
if (derivedStateFromCatch !== null && derivedStateFromCatch !== undefined) { | ||
// Render-phase updates (like this) should not be added to the update queue, | ||
// So that multiple render passes do not enqueue multiple updates. | ||
// Instead, just synchronously merge the returned state into the instance. | ||
newState = | ||
newState === null || newState === undefined | ||
? derivedStateFromProps | ||
: Object.assign({}, newState, derivedStateFromProps); | ||
? derivedStateFromCatch | ||
: Object.assign({}, newState, derivedStateFromCatch); | ||
} | ||
if (derivedStateFromCatch !== null && derivedStateFromCatch !== undefined) { | ||
|
||
let derivedStateFromProps; | ||
if (oldProps !== newProps) { | ||
// In this case, the prevState parameter should be the partially updated state, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Conceptually, what you're calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "in this case" just referred to the update case. The other two invocations of this method are mounting cases. But I'll re-word the comment. |
||
// Otherwise, spreading state in return values could override updates. | ||
derivedStateFromProps = callGetDerivedStateFromProps( | ||
workInProgress, | ||
instance, | ||
newProps, | ||
newState, | ||
); | ||
} | ||
|
||
if (derivedStateFromProps !== null && derivedStateFromProps !== undefined) { | ||
// Render-phase updates (like this) should not be added to the update queue, | ||
// So that multiple render passes do not enqueue multiple updates. | ||
// Instead, just synchronously merge the returned state into the instance. | ||
newState = | ||
newState === null || newState === undefined | ||
? derivedStateFromCatch | ||
: Object.assign({}, newState, derivedStateFromCatch); | ||
? derivedStateFromProps | ||
: Object.assign({}, newState, derivedStateFromProps); | ||
} | ||
|
||
if ( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: let's not use
findDOMNode
. Maybe a ref instead? I know it's just a test, but we should still try to avoid using legacy APIs :DThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I confess, I often copy paste an existing test when writing a new one to save a few seconds.