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 |
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
onStoreChangeupdates 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'suseIsomorphicLayoutEffectreferencing a stale value.A specific example of this issue occurring is the following code where
onUnselectis called. This call triggers an update to the store the occurs in betweenuseSelect's reading and writing the selector value, resulting in a staleisTitleSelectedvalue. 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