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

[fresh] Reset useMemoCache when size changes #30663

Closed
wants to merge 11 commits into from

Conversation

poteto
Copy link
Member

@poteto poteto commented Aug 12, 2024

Stack from ghstack (oldest at bottom):

During development when adding or removing code, it is common for the
cache size to vary between refreshes. Rather than warn like before,
let's simply and safely reset the cache completely whenever its size
changes between renders. In practice, in non-development environments
full invalidation should never happen since the only consumer of uMC is
compiled code – which always emits a static call to uMC with a fixed
size. However, for consistency we continue to reset the cache as well.

[ghstack-poisoned]
Copy link

vercel bot commented Aug 12, 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 Aug 14, 2024 6:54pm

poteto added a commit that referenced this pull request Aug 12, 2024
During development when adding or removing code, it is common for the
cache size to vary between refreshes. Rather than warn like before,
let's simply and safely reset the cache completely whenever its size
changes between renders. In practice, in non-development environments
full invalidation should never happen since the only consumer of uMC is
compiled code – which always emits a static call to uMC with a fixed
size.

ghstack-source-id: 2f17b959d126672751f1c9b2fe0026800d8ba174
Pull Request resolved: #30663
@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Aug 12, 2024
@react-sizebot
Copy link

react-sizebot commented Aug 12, 2024

Comparing: dd8e0ba...e007738

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 +0.11% 1.82 kB 1.83 kB
oss-stable/react-dom/cjs/react-dom-client.production.js = 500.37 kB 500.40 kB +0.01% 89.80 kB 89.81 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.69 kB 6.69 kB +0.05% 1.83 kB 1.83 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js = 507.50 kB 507.53 kB +0.01% 90.96 kB 90.97 kB
facebook-www/ReactDOM-prod.classic.js = 595.24 kB 595.27 kB = 105.55 kB 105.56 kB
facebook-www/ReactDOM-prod.modern.js = 571.54 kB 571.57 kB +0.01% 101.75 kB 101.76 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
test_utils/ReactAllWarnings.js = 64.07 kB 63.92 kB = 15.86 kB 15.81 kB

Generated by 🚫 dangerJS against 237ebef

Comment on lines 1237 to 1240
data = memoCache.data[memoCache.index] = new Array(size);
for (let i = 0; i < size; i++) {
data[i] = REACT_MEMO_CACHE_SENTINEL;
}
Copy link
Member Author

@poteto poteto Aug 12, 2024

Choose a reason for hiding this comment

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

An alternative approach might be to append a debug property on the fiber during fast refresh to reset the cache only when Fresh actually kicks in. That has the advantage of resetting the cache during a refresh even when cache size is the same (ie new code happened to result in the same number of cache slots)

Copy link
Member

Choose a reason for hiding this comment

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

This seems to be the better approach to me.

  • React shouldn't be throwing prod-only errors.
  • Feels very likely there are types of changes where the memo size doesn't change (e.g. some literal such as a style is updated in the component)

Copy link
Collaborator

Choose a reason for hiding this comment

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

What does useMemo do if its implementation changes? Does it just keep its cache entry? Seems like it would need to reset for the same reason.

Copy link
Collaborator

Choose a reason for hiding this comment

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

useMemo always drops the cache on hot reload. Seems like we should do the same for uMC? We assume memoization is not essential for the logic and can be reconstructed. This also tests that the component logic is resilient to it somewhat.

Copy link
Collaborator

Choose a reason for hiding this comment

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

[ghstack-poisoned]
poteto added a commit that referenced this pull request Aug 12, 2024
During development when adding or removing code, it is common for the
cache size to vary between refreshes. Rather than warn like before,
let's simply and safely reset the cache completely whenever its size
changes between renders. In practice, in non-development environments
full invalidation should never happen since the only consumer of uMC is
compiled code – which always emits a static call to uMC with a fixed
size.

ghstack-source-id: 48c5b9c848a0b9ed01576ad357e067b72e8f887f
Pull Request resolved: #30663
[ghstack-poisoned]
poteto added a commit that referenced this pull request Aug 12, 2024
During development when adding or removing code, it is common for the
cache size to vary between refreshes. Rather than warn like before,
let's simply and safely reset the cache completely whenever its size
changes between renders. In practice, in non-development environments
full invalidation should never happen since the only consumer of uMC is
compiled code – which always emits a static call to uMC with a fixed
size.

ghstack-source-id: bffbd19f2b0fad935d2c51e6c3f129bd5900b9be
Pull Request resolved: #30663
[ghstack-poisoned]
poteto added a commit that referenced this pull request Aug 12, 2024
During development when adding or removing code, it is common for the
cache size to vary between refreshes. Rather than warn like before,
let's simply and safely reset the cache completely whenever its size
changes between renders. In practice, in non-development environments
full invalidation should never happen since the only consumer of uMC is
compiled code – which always emits a static call to uMC with a fixed
size.

ghstack-source-id: 48c5b9c848a0b9ed01576ad357e067b72e8f887f
Pull Request resolved: #30663
@poteto poteto requested a review from josephsavona August 12, 2024 15:52
@poteto poteto marked this pull request as ready for review August 12, 2024 15:52
[ghstack-poisoned]
poteto added a commit that referenced this pull request Aug 12, 2024
During development when adding or removing code, it is common for the
cache size to vary between refreshes. Rather than warn like before,
let's simply and safely reset the cache completely whenever its size
changes between renders. In practice, in non-development environments
full invalidation should never happen since the only consumer of uMC is
compiled code – which always emits a static call to uMC with a fixed
size.

ghstack-source-id: 3fa62480e8bc89fc3c8593937be75290864d88d3
Pull Request resolved: #30663
Copy link
Contributor

@josephsavona josephsavona left a comment

Choose a reason for hiding this comment

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

Throwing in prod for something that works in dev can lead to problems. I think we might need to either a) specifically limit this to fast refresh in dev (so that it errors otherwise) or b) just reset even in prod.

But I can see the argument that since you are never supposed to use this API directly, that it's maybe fine for it to have different behavior in dev/prod.

@poteto
Copy link
Member Author

poteto commented Aug 12, 2024

@josephsavona Yeah I was kinda torn about it but I was thinking that there should be no case ever where the cache size would change in between renders in prod, so it's better to error early. Alternatively (as you also pointed out in (b)) we can always reset the cache if the size changes in any environment, which would then eliminate the dev/prod divergence

@josephsavona
Copy link
Contributor

Per offline discussion let's go with resetting whenever the size changes. Seems like we're agreed that the ideal way to do this is for React to get a signal that a particular component fast refreshed and to reset the cache only then. But let's save that for a follow-up.

[ghstack-poisoned]
poteto added a commit that referenced this pull request Aug 13, 2024
During development when adding or removing code, it is common for the
cache size to vary between refreshes. Rather than warn like before,
let's simply and safely reset the cache completely whenever its size
changes between renders. In practice, in non-development environments
full invalidation should never happen since the only consumer of uMC is
compiled code – which always emits a static call to uMC with a fixed
size. However, for consistency we continue to reset the cache as well.

ghstack-source-id: b295a5d79692484f8685974dea2d6ce3954ef855
Pull Request resolved: #30663
poteto added a commit that referenced this pull request Aug 13, 2024
During development when adding or removing code, it is common for the
cache size to vary between refreshes. Rather than warn like before,
let's simply and safely reset the cache completely whenever its size
changes between renders. In practice, in non-development environments
full invalidation should never happen since the only consumer of uMC is
compiled code – which always emits a static call to uMC with a fixed
size. However, for consistency we continue to reset the cache as well.

ghstack-source-id: b295a5d79692484f8685974dea2d6ce3954ef855
Pull Request resolved: #30663
[ghstack-poisoned]
poteto added a commit that referenced this pull request Aug 13, 2024
During development when adding or removing code, it is common for the
cache size to vary between refreshes. Rather than warn like before,
let's simply and safely reset the cache completely whenever its size
changes between renders. In practice, in non-development environments
full invalidation should never happen since the only consumer of uMC is
compiled code – which always emits a static call to uMC with a fixed
size. However, for consistency we continue to reset the cache as well.

ghstack-source-id: 7f343a7e7d4c0575146c9c9148ab2d34137d578d
Pull Request resolved: #30663
[ghstack-poisoned]
poteto added a commit that referenced this pull request Aug 13, 2024
During development when adding or removing code, it is common for the
cache size to vary between refreshes. Rather than warn like before,
let's simply and safely reset the cache completely whenever its size
changes between renders. In practice, in non-development environments
full invalidation should never happen since the only consumer of uMC is
compiled code – which always emits a static call to uMC with a fixed
size. However, for consistency we continue to reset the cache as well.

ghstack-source-id: 440a8997bf61448ac01ce78d5f58e1e1b880fa83
Pull Request resolved: #30663
@poteto
Copy link
Member Author

poteto commented Aug 13, 2024

@josephsavona updated

[ghstack-poisoned]
@gaearon
Copy link
Collaborator

gaearon commented Aug 14, 2024

Why is it not sufficient to always throw away the cache on hot reload? Unless I’m missing something, that’s what we do for useMemo.

@poteto
Copy link
Member Author

poteto commented Aug 14, 2024

@gaearon that's #30677

poteto added 2 commits August 14, 2024 14:44
[ghstack-poisoned]
[ghstack-poisoned]
Copy link
Collaborator

@gaearon gaearon left a comment

Choose a reason for hiding this comment

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

I think #30700 should take care of this, assuming the special case is only needed for Fast Refresh.

@poteto
Copy link
Member Author

poteto commented Aug 14, 2024

Closing in favor of #30700

@poteto poteto closed this Aug 14, 2024
gaearon added a commit that referenced this pull request Aug 15, 2024
Stacked on #30662.

Alternative to #30663 and
#30677.

During a Fast Refresh, we always want to evict the memo cache, same as
we do with normal `useMemo`. The mechanism used by `useMemo` and other
Hooks is this module-level variable:


https://github.com/facebook/react/blob/fca5d655d78917400a2722287351c20938166669/packages/react-reconciler/src/ReactFiberHooks.js#L304-L307

which has DEV-only behavior as if the dependencies are always different:


https://github.com/facebook/react/blob/fca5d655d78917400a2722287351c20938166669/packages/react-reconciler/src/ReactFiberHooks.js#L451-L460

The `useMemoCache` Hook doesn't use a dependency array but conceptually
I think we want the same behavior.

## Test Plan

The test passes.

---------

Co-authored-by: Lauren Tan <poteto@users.noreply.github.com>
github-actions bot pushed a commit that referenced this pull request Aug 15, 2024
Stacked on #30662.

Alternative to #30663 and
#30677.

During a Fast Refresh, we always want to evict the memo cache, same as
we do with normal `useMemo`. The mechanism used by `useMemo` and other
Hooks is this module-level variable:

https://github.com/facebook/react/blob/fca5d655d78917400a2722287351c20938166669/packages/react-reconciler/src/ReactFiberHooks.js#L304-L307

which has DEV-only behavior as if the dependencies are always different:

https://github.com/facebook/react/blob/fca5d655d78917400a2722287351c20938166669/packages/react-reconciler/src/ReactFiberHooks.js#L451-L460

The `useMemoCache` Hook doesn't use a dependency array but conceptually
I think we want the same behavior.

## Test Plan

The test passes.

---------

Co-authored-by: Lauren Tan <poteto@users.noreply.github.com>

DiffTrain build for [7e8a06c](7e8a06c)
github-actions bot pushed a commit that referenced this pull request Aug 15, 2024
Stacked on #30662.

Alternative to #30663 and
#30677.

During a Fast Refresh, we always want to evict the memo cache, same as
we do with normal `useMemo`. The mechanism used by `useMemo` and other
Hooks is this module-level variable:

https://github.com/facebook/react/blob/fca5d655d78917400a2722287351c20938166669/packages/react-reconciler/src/ReactFiberHooks.js#L304-L307

which has DEV-only behavior as if the dependencies are always different:

https://github.com/facebook/react/blob/fca5d655d78917400a2722287351c20938166669/packages/react-reconciler/src/ReactFiberHooks.js#L451-L460

The `useMemoCache` Hook doesn't use a dependency array but conceptually
I think we want the same behavior.

## Test Plan

The test passes.

---------

Co-authored-by: Lauren Tan <poteto@users.noreply.github.com>

DiffTrain build for commit 7e8a06c.
@poteto poteto deleted the gh/poteto/2/head branch October 7, 2024 22:08
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.

7 participants