Skip to content

Conversation

@barryam3
Copy link

@barryam3 barryam3 commented Sep 20, 2025

Summary

Fixes #33793

This widens the SimpleMemoComponent fast path introduced in #13903 to support memoized function components with custom props comparisons. The fast path eliminates the need for the wrapper MemoComponent to be tracked as a separate Fiber, reducing performance overhead.

In addition, React DevTools will show only one node for the component instead of two (because there is only one Fiber instead of two), which makes debugging easier by reducing visual and therefore mental clutter.

I considered several approaches as discussed in the issue before settling on this one as the best combination of simplicity, safety, and performance after benchmarking and prototyping.

How did you test this change?

Per the contributing instructions:

  1. yarn test - two tests fail ("ReactFlightAsyncDebugInfo › can track async information when awaited" and "ReactFlightDOMEdge › should execute repeated host components only once") but these also fail at main.
  2. yarn test --prod - 59 test failures but again this is the same as main. Here they are if you want to see: https://gist.github.com/barryam3/82007cf5ef6b1c27621e0395ba9a1e71
  3. yarn prettier
  4. yarn lint
  5. yarn flow dom-node (I first tried yarn flow as recommended by the PR template, but it told me to pick a renderer, and dom-node is a good default)

Additionally I updated the DevTools unit tests to reflect the component tree in DevTools.

  1. yarn run build-for-devtools
  2. yarn run test-build-devtools - two tests fail ("ignoreList source map extension › for dev builds › should not ignore list anything" and "ignoreList source map extension › for production builds › should include every source") but these also fail at main

I made the equivalent change in my own application (thousands of components, hundreds memoized with custom arePropsEqual) via a pnpm patch and have not discovered any issues after running in production for about 2 months. I verified that this solves the "extra node" problem in DevTools. (In the interest of sharing any possible caveats, my app is still on React 18.2.0 and I chose a slightly simpler implementation for the patch of just setting a compare field on the function instead of using a Weak Map.)

@barryam3 barryam3 force-pushed the memo-fastpath-weakmap branch from 9e3c4ba to cc0ba0b Compare November 5, 2025 02:22
@react-sizebot
Copy link

Comparing: 8f8b336...cc0ba0b

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.83 kB
oss-stable/react-dom/cjs/react-dom-client.production.js +0.05% 606.23 kB 606.56 kB +0.07% 107.39 kB 107.46 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 +0.05% 665.20 kB 665.53 kB +0.06% 117.26 kB 117.34 kB
facebook-www/ReactDOM-prod.classic.js +0.05% 690.02 kB 690.36 kB +0.07% 121.46 kB 121.55 kB
facebook-www/ReactDOM-prod.modern.js +0.05% 680.45 kB 680.78 kB +0.06% 119.85 kB 119.92 kB

Significant size changes

Includes any change greater than 0.2%:

(No significant changes)

Generated by 🚫 dangerJS against cc0ba0b

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: SimpleMemoComponent fast path for React.memo with custom arePropsEqual

2 participants