-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Capture state changes scheduled between render and effect #38509
Capture state changes scheduled between render and effect #38509
Conversation
Size Change: +2.47 kB (0%) Total Size: 1.26 MB
ℹ️ View Unchanged
|
8b4c4d7
to
3987f61
Compare
9fb39e1
to
bd255ac
Compare
In the event that a child component dispatched an action modifying the store, the changes in the store were lost due to `useSelect`'s `useIsomorphicLayoutEffect` referencing a stale value.
In the event that a child component dispatched an action modifying the store, the changes in the store were lost due to `useSelect`'s `useIsomorphicLayoutEffect` referencing a stale value. This change ensures the `useIsomorphicLayoutEffect` references the latest `mapOutput` when setting the related `latestMapOutput.current` value.
Store updates triggered from within the post title component's `componentDidUpdate` hook resulted in stale `isTitledSelected` values returned from the memoized `useSelect`. This reverts a workaround that avoided memoization by splitting the `useSelect` hook usage in two.
f804f04
to
3172233
Compare
Hey @nerrad and @youknowriad. 👋🏻 This change touches fairly low-level logic. From what I found, you both have discussed the existing code a good bit. I would appreciate your perspective on these changes. It is not a rush, so please review at your convenience. Thank you! 🙇🏻 |
Hi @dcalhoun 👋 seems you're really up to something 🙂 The // read the current value
const mapOutput = latestMapOutput.current;
// indiscriminately write the read value back into the ref in an effect
useIsomorphicLayoutEffect( () => {
latestMapOutput.current = mapOutput;
} );
// write to the ref also in subscription callback
subscribe( function onStoreChange() {
latestMapOutput.current = newValue;
} ); Your test example works in such a way that the
At the moment when the action is being dispatched the |
I remembered that the I rewrote the unit test that this PR is adding to Redux and observed that a stale value is really written:
So, the stale value is written, but it will never be used. On the other hand, |
This avoids stale mappings overwriting new values in specific contexts where `onStoreChange` updates the value before the effect writes the original mapping.
Thank you for the thorough review and investigation, @jsnajdr. 🙇🏻
This makes sense now that you explain it so clearly.
This is very interesting, and very educational for me in regards to how |
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.
Looks good 👍
This test focuses upon the order in which state is read and written. It is not related to when the subscription is set.
Reduce cognitive load of code by grouping related logic.
What?
Fixes #32154. Supersedes #32831. Avoid stale mappings overwriting new state in specific contexts where
onStoreChange
updates the state before the effect writes the initial mapping.Why?
In the event that a child component dispatched an action modifying the store before the selector mapping was written, the changes in the store were lost due to
useSelect
'suseIsomorphicLayoutEffect
referencing a stale value.A specific example of this issue occurring is the following code where
onUnselect
is called. This call triggers an update to the store the occurs in betweenuseSelect
's reading and writing the selector value, resulting in a staleisTitleSelected
value. This resulted in the blocks inserted to the incorrect location, which is captured in #32154.gutenberg/packages/editor/src/components/post-title/index.native.js
Lines 36 to 48 in 3987f61
gutenberg/packages/editor/src/components/provider/use-block-editor-settings.native.js
Line 31 in 3987f61
How?
Avoid setting a reference to the selector output mapping until after the selector is run.
Testing Instructions
The following was manually tested:
Screenshots or screencast
Before
before.mov
After
after.mov