Skip to content
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

Fix useMemoCache with setState in render #30889

Merged
merged 12 commits into from
Sep 6, 2024

Conversation

josephsavona
Copy link
Contributor

@josephsavona josephsavona commented Sep 5, 2024

Stack from ghstack (oldest at bottom):

Fixes the bug that @alexmckenley and @mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

I initially changed renderWithHooksAgain() to just reset the updateQueue, nulling out properties and setting the memoCache.index back to 0, but not clearing memoCache.data. That fixed the uMC bug, but caused some tests to fail where effect cleanup wasn’t running when a component was in a tree that suspended and showed a fallback.

Turns out renderWithHooksAgain() is also used to re-render suspended components, and that case depends on the updateQueue being cleared for effects to work correctly. So i updated to clear the updateQueue for the suspense case, reset in the setState-in-render case, and everything works. The result feels pretty clean.

In a follow-up we should be able to avoid resetting the memo cache in the replay case as well. There are a few spots in the codebase where we check for updateQueue === null, and only do processing in that case — including some code related to effect cleanup. I think the right thing to do there would be to check for e.g. updateQueue?.lastEffect == null (and any other relevant properties from updateQueue), and then it would be safe to always reset the updateQueue instead of clearing it.

Copy link

vercel bot commented Sep 5, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
react-compiler-playground ✅ Ready (Inspect) Visit Preview 💬 Add feedback Sep 6, 2024 9:13pm

josephsavona added a commit that referenced this pull request Sep 5, 2024
ghstack-source-id: b3e3b825955163045bc7a4f3df76b40263f728a0
Pull Request resolved: #30889
@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Sep 5, 2024
Fixes the bug that alexmckenley and mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

[ghstack-poisoned]
@react-sizebot
Copy link

react-sizebot commented Sep 5, 2024

Comparing: bd788b4...7de65dd

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 6.68 kB 6.68 kB = 1.83 kB 1.82 kB
oss-stable/react-dom/cjs/react-dom-client.production.js = 505.04 kB 503.41 kB = 90.26 kB 90.27 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.69 kB 6.69 kB = 1.83 kB 1.83 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js = 512.23 kB 510.62 kB = 91.47 kB 91.45 kB
facebook-www/ReactDOM-prod.classic.js = 600.56 kB 598.74 kB = 106.23 kB 106.09 kB
facebook-www/ReactDOM-prod.modern.js = 576.85 kB 575.03 kB = 102.37 kB 102.27 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
facebook-www/ReactDOM-profiling.classic.js = 630.15 kB 628.77 kB = 110.69 kB 110.55 kB
react-native/implementations/ReactNativeRenderer-profiling.js = 385.95 kB 385.09 kB = 67.08 kB 66.99 kB
facebook-www/ReactDOM-profiling.modern.js = 605.80 kB 604.42 kB = 106.69 kB 106.58 kB
react-native/implementations/ReactNativeRenderer-dev.js = 641.37 kB 639.90 kB = 105.49 kB 105.26 kB
oss-stable/react-reconciler/cjs/react-reconciler.production.js = 380.64 kB 379.62 kB +0.03% 62.39 kB 62.41 kB
oss-stable-rc/react-reconciler/cjs/react-reconciler.production.js = 380.62 kB 379.60 kB +0.03% 62.37 kB 62.39 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler.production.js = 380.62 kB 379.60 kB +0.03% 62.37 kB 62.39 kB
react-native/implementations/ReactNativeRenderer-dev.fb.js = 664.72 kB 662.87 kB = 109.16 kB 108.90 kB
react-native/implementations/ReactNativeRenderer-profiling.fb.js = 410.06 kB 408.92 kB = 71.23 kB 71.12 kB
oss-stable/react-dom/cjs/react-dom-profiling.development.js = 952.40 kB 949.71 kB = 161.30 kB 161.14 kB
oss-stable-rc/react-dom/cjs/react-dom-profiling.development.js = 952.30 kB 949.60 kB = 161.27 kB 161.11 kB
oss-stable-semver/react-dom/cjs/react-dom-profiling.development.js = 952.30 kB 949.60 kB = 161.27 kB 161.11 kB
react-native/implementations/ReactFabric-prod.fb.js = 377.24 kB 376.17 kB = 65.82 kB 65.71 kB
react-native/implementations/ReactFabric-prod.js = 349.50 kB 348.51 kB = 61.24 kB 61.14 kB
oss-stable/react-dom/cjs/react-dom-client.development.js = 935.96 kB 933.27 kB = 158.46 kB 158.32 kB
oss-stable-rc/react-dom/cjs/react-dom-client.development.js = 935.86 kB 933.16 kB = 158.43 kB 158.29 kB
oss-stable-semver/react-dom/cjs/react-dom-client.development.js = 935.86 kB 933.16 kB = 158.43 kB 158.29 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.development.js = 966.92 kB 964.09 kB = 164.35 kB 164.18 kB
oss-experimental/react-dom/cjs/react-dom-profiling.development.js = 966.42 kB 963.60 kB = 163.35 kB 163.18 kB
oss-experimental/react-reconciler/cjs/react-reconciler.development.js = 646.92 kB 645.01 kB = 103.84 kB 103.67 kB
oss-stable/react-reconciler/cjs/react-reconciler.development.js = 633.36 kB 631.48 kB = 101.98 kB 101.80 kB
oss-stable-rc/react-reconciler/cjs/react-reconciler.development.js = 633.34 kB 631.46 kB = 101.96 kB 101.78 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler.development.js = 633.34 kB 631.46 kB = 101.96 kB 101.78 kB
facebook-www/ReactDOMTesting-prod.classic.js = 615.30 kB 613.47 kB = 109.92 kB 109.81 kB
facebook-www/ReactReconciler-dev.classic.js = 739.04 kB 736.85 kB = 118.07 kB 117.85 kB
oss-experimental/react-dom/cjs/react-dom-client.development.js = 949.98 kB 947.15 kB = 160.52 kB 160.36 kB
facebook-www/ReactDOMTesting-dev.classic.js = 1,101.40 kB 1,098.06 kB = 185.93 kB 185.57 kB
facebook-www/ReactDOM-prod.classic.js = 600.56 kB 598.74 kB = 106.23 kB 106.09 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.production.js = 526.96 kB 525.35 kB = 95.15 kB 95.13 kB
facebook-www/ReactDOM-dev.classic.js = 1,084.49 kB 1,081.15 kB = 181.96 kB 181.62 kB
facebook-www/ReactDOMTesting-prod.modern.js = 591.58 kB 589.76 kB = 106.08 kB 105.99 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js = 512.23 kB 510.62 kB = 91.47 kB 91.45 kB
facebook-www/ReactDOM-prod.modern.js = 576.85 kB 575.03 kB = 102.37 kB 102.27 kB
oss-stable/react-dom/cjs/react-dom-client.production.js = 505.04 kB 503.41 kB = 90.26 kB 90.27 kB
oss-stable-rc/react-dom/cjs/react-dom-client.production.js = 504.93 kB 503.30 kB = 90.24 kB 90.24 kB
oss-stable-semver/react-dom/cjs/react-dom-client.production.js = 504.93 kB 503.30 kB = 90.24 kB 90.24 kB
facebook-react-native/react-test-renderer/cjs/ReactTestRenderer-profiling.js = 345.77 kB 344.55 kB = 60.27 kB 60.13 kB
react-native/implementations/ReactNativeRenderer-prod.js = 359.20 kB 357.84 kB = 62.90 kB 62.78 kB
facebook-www/ReactDOMTesting-dev.modern.js = 1,065.55 kB 1,061.24 kB = 179.91 kB 179.48 kB
facebook-www/ReactDOM-dev.modern.js = 1,048.64 kB 1,044.33 kB = 176.04 kB 175.60 kB
facebook-react-native/react-test-renderer/cjs/ReactTestRenderer-dev.js = 579.38 kB 577.00 kB = 94.62 kB 94.23 kB
react-native/implementations/ReactNativeRenderer-prod.fb.js = 383.02 kB 381.37 kB = 67.04 kB 66.87 kB
facebook-www/ReactReconciler-dev.modern.js = 715.19 kB 712.09 kB = 114.03 kB 113.72 kB
facebook-www/ReactART-prod.classic.js = 367.66 kB 365.98 kB = 62.19 kB 62.11 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js = 548.67 kB 546.15 kB = 89.90 kB 89.44 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js = 546.29 kB 543.78 kB = 89.49 kB 89.06 kB
oss-stable-rc/react-test-renderer/cjs/react-test-renderer.development.js = 546.23 kB 543.71 kB = 89.47 kB 89.03 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js = 546.23 kB 543.71 kB = 89.47 kB 89.03 kB
oss-experimental/react-art/cjs/react-art.production.js = 302.20 kB 300.78 kB = 51.97 kB 51.83 kB
facebook-www/ReactART-prod.modern.js = 350.65 kB 348.97 kB = 59.44 kB 59.35 kB
facebook-react-native/react-test-renderer/cjs/ReactTestRenderer-prod.js = 322.40 kB 320.65 kB = 56.80 kB 56.66 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.js = 307.17 kB 305.42 kB = 54.39 kB 54.21 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.js = 304.97 kB 303.22 kB = 53.95 kB 53.77 kB
oss-stable-rc/react-test-renderer/cjs/react-test-renderer.production.js = 304.91 kB 303.16 kB = 53.92 kB 53.75 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.js = 304.91 kB 303.16 kB = 53.92 kB 53.75 kB
oss-stable/react-art/cjs/react-art.production.js = 295.74 kB 294.04 kB = 50.80 kB 50.62 kB
oss-stable-rc/react-art/cjs/react-art.production.js = 295.68 kB 293.98 kB = 50.78 kB 50.60 kB
oss-stable-semver/react-art/cjs/react-art.production.js = 295.68 kB 293.98 kB = 50.78 kB 50.60 kB
facebook-www/ReactTestRenderer-dev.classic.js = 573.40 kB 569.90 kB = 93.95 kB 93.46 kB
facebook-www/ReactTestRenderer-dev.modern.js = 573.39 kB 569.90 kB = 93.95 kB 93.46 kB
facebook-www/ReactART-dev.classic.js = 652.62 kB 648.58 kB = 104.84 kB 104.27 kB
oss-experimental/react-art/cjs/react-art.development.js = 565.04 kB 560.85 kB = 91.23 kB 90.73 kB
oss-stable/react-art/cjs/react-art.development.js = 551.02 kB 546.90 kB = 89.39 kB 88.92 kB
oss-stable-rc/react-art/cjs/react-art.development.js = 550.95 kB 546.84 kB = 89.37 kB 88.90 kB
oss-stable-semver/react-art/cjs/react-art.development.js = 550.95 kB 546.84 kB = 89.37 kB 88.90 kB
facebook-www/ReactART-dev.modern.js = 630.18 kB 625.25 kB = 101.16 kB 100.53 kB

Generated by 🚫 dangerJS against 03000dd

josephsavona added a commit that referenced this pull request Sep 5, 2024
Fixes the bug that @alexmckenley and @mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

ghstack-source-id: b3e3b825955163045bc7a4f3df76b40263f728a0
Pull Request resolved: #30889
Fixes the bug that alexmckenley and mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request Sep 5, 2024
Fixes the bug that @alexmckenley and @mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

ghstack-source-id: a71a7832188ef80b5c55c9f80b641282648ec735
Pull Request resolved: #30889
Fixes the bug that alexmckenley and mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request Sep 5, 2024
Fixes the bug that @alexmckenley and @mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

ghstack-source-id: 62875e642be1f29b66354f812f06f8f0ce4b1b66
Pull Request resolved: #30889
Fixes the bug that alexmckenley and mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request Sep 5, 2024
Fixes the bug that @alexmckenley and @mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

ghstack-source-id: 8bab4b4802284dc464a620b415bfb21b5ca52508
Pull Request resolved: #30889
Fixes the bug that alexmckenley and mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request Sep 5, 2024
Fixes the bug that @alexmckenley and @mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

ghstack-source-id: 4c6350a60286b92113ee5922645406fa94b7b34a
Pull Request resolved: #30889
Copy link
Member

@kassens kassens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense

…r on "Fix useMemoCache with setState in render"

Fixes the bug that alexmckenley and mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request Sep 5, 2024
Fixes the bug that @alexmckenley and @mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

ghstack-source-id: 4d25c920015ba7fb2d1014f01886baa6979e3d96
Pull Request resolved: #30889
Comment on lines 638 to 642
// Because we clone/reset the memo cache after every aborted attempt, we
// must process the first chunk again.
//
// That's three total times we've processed the first chunk, compared to
// just once when enableNoCloningMemoCache is on.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like this reset was just an artifact of the renderWithHooksAgain() reset. so the feature flag helps, but a bit less than we expected (we process the chunk once instead of twice)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm nope, more complicated than that. Tests seem to be failing on persistent mode with this change, still debugging.

Fixes the bug that alexmckenley and mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request Sep 6, 2024
Fixes the bug that @alexmckenley and @mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

ghstack-source-id: d0508abb7babfcd7d96a166eabb4f8a7e19c9c54
Pull Request resolved: #30889
Fixes the bug that alexmckenley and mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request Sep 6, 2024
Fixes the bug that @alexmckenley and @mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

ghstack-source-id: 57608c8a4d5ba86de26b9a4e1d3a9c4d5463b0e4
Pull Request resolved: #30889
Fixes the bug that alexmckenley and mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request Sep 6, 2024
Fixes the bug that @alexmckenley and @mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

ghstack-source-id: ab48eacccff535616dcd4a058637feb60d350380
Pull Request resolved: #30889
Fixes the bug that alexmckenley and mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request Sep 6, 2024
Fixes the bug that @alexmckenley and @mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

ghstack-source-id: 50bf0cebef9fbc1010a26f74ab5003be49df06e0
Pull Request resolved: #30889
…tate in render"

Fixes the bug that alexmckenley and mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request Sep 6, 2024
Fixes the bug that @alexmckenley and @mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

ghstack-source-id: fc0947ce219334117075df6a4e33b39975af2bc4
Pull Request resolved: #30889
Comment on lines +2523 to +2531
}
const lastEffect = componentUpdateQueue.lastEffect;
if (lastEffect === null) {
componentUpdateQueue.lastEffect = effect.next = effect;
} else {
const lastEffect = componentUpdateQueue.lastEffect;
if (lastEffect === null) {
componentUpdateQueue.lastEffect = effect.next = effect;
} else {
const firstEffect = lastEffect.next;
lastEffect.next = effect;
effect.next = firstEffect;
componentUpdateQueue.lastEffect = effect;
}
const firstEffect = lastEffect.next;
lastEffect.next = effect;
effect.next = firstEffect;
componentUpdateQueue.lastEffect = effect;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drive by cleanup since i was here

@josephsavona josephsavona merged commit 03000dd into gh/josephsavona/56/base Sep 6, 2024
184 checks passed
josephsavona added a commit that referenced this pull request Sep 6, 2024
Fixes the bug that @alexmckenley and @mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

ghstack-source-id: fc0947ce219334117075df6a4e33b39975af2bc4
Pull Request resolved: #30889
@josephsavona josephsavona deleted the gh/josephsavona/56/head branch September 6, 2024 21:32
github-actions bot pushed a commit that referenced this pull request Sep 6, 2024
Fixes the bug that @alexmckenley and @mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

ghstack-source-id: fc0947ce219334117075df6a4e33b39975af2bc4
Pull Request resolved: #30889

DiffTrain build for commit 727b361.
github-actions bot pushed a commit that referenced this pull request Sep 6, 2024
Fixes the bug that @alexmckenley and @mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.

The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.

ghstack-source-id: fc0947ce219334117075df6a4e33b39975af2bc4
Pull Request resolved: #30889

DiffTrain build for [727b361](727b361)
gnoff pushed a commit to vercel/next.js that referenced this pull request Sep 12, 2024
**breaking change for canary users: Bumps peer dependency of React from
`19.0.0-rc-7771d3a7-20240827` to `19.0.0-rc-94e652d5-20240912`**

[diff
facebook/react@7771d3a7...94e652d5](facebook/react@7771d3a...94e652d)

<details>
<summary>React upstream changes</summary>

- facebook/react#30952
- facebook/react#30950
- facebook/react#30946
- facebook/react#30934
- facebook/react#30947
- facebook/react#30945
- facebook/react#30938
- facebook/react#30936
- facebook/react#30879
- facebook/react#30888
- facebook/react#30931
- facebook/react#30930
- facebook/react#30832
- facebook/react#30929
- facebook/react#30926
- facebook/react#30925
- facebook/react#30905
- facebook/react#30900
- facebook/react#30910
- facebook/react#30906
- facebook/react#30899
- facebook/react#30919
- facebook/react#30708
- facebook/react#30907
- facebook/react#30897
- facebook/react#30896
- facebook/react#30895
- facebook/react#30887
- facebook/react#30889
- facebook/react#30893
- facebook/react#30892
- facebook/react#30891
- facebook/react#30882
- facebook/react#30881
- facebook/react#30870
- facebook/react#30849
- facebook/react#30878
- facebook/react#30865
- facebook/react#30869
- facebook/react#30875
- facebook/react#30800
- facebook/react#30762
- facebook/react#30831
- facebook/react#30866
- facebook/react#30853
- facebook/react#30850
- facebook/react#30847
- facebook/react#30842
- facebook/react#30837
- facebook/react#30848
- facebook/react#30844
- facebook/react#30839
- facebook/react#30802
- facebook/react#30841
- facebook/react#30827
- facebook/react#30826
- facebook/react#30825
- facebook/react#30824
- facebook/react#30840
- facebook/react#30838
- facebook/react#30836
- facebook/react#30819
- facebook/react#30816
- facebook/react#30814
- facebook/react#30813
- facebook/react#30812
- facebook/react#30811

</details>

---------

Co-authored-by: vercel-release-bot <infra+release@vercel.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants