From 6d510c328b6a25cca24fd626208a308894ae991e Mon Sep 17 00:00:00 2001 From: Sebastian Sebbie Silbermann Date: Mon, 13 Oct 2025 09:45:37 +0200 Subject: [PATCH 1/2] Current behavior for uEE in React.memo --- .../src/__tests__/useEffectEvent-test.js | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/packages/react-reconciler/src/__tests__/useEffectEvent-test.js b/packages/react-reconciler/src/__tests__/useEffectEvent-test.js index 17b8d6d421f..edee01ef634 100644 --- a/packages/react-reconciler/src/__tests__/useEffectEvent-test.js +++ b/packages/react-reconciler/src/__tests__/useEffectEvent-test.js @@ -850,4 +850,43 @@ describe('useEffectEvent', () => { ); assertLog(['Add to cart', 'url: /shop/2, numberOfItems: 1']); }); + + it('reads the latest context value in memo Components', async () => { + const MyContext = createContext('default'); + + let logContextValue; + const ContextReader = React.memo(function ContextReader() { + const value = useContext(MyContext); + Scheduler.log('ContextReader: ' + value); + const fireLogContextValue = useEffectEvent(() => { + Scheduler.log('ContextReader (Effect event): ' + value); + }); + useEffect(() => { + logContextValue = fireLogContextValue; + }, []); + return null; + }); + + function App({value}) { + return ( + + + + ); + } + + const root = ReactNoop.createRoot(); + await act(() => root.render()); + assertLog(['ContextReader: first']); + + logContextValue(); + + assertLog(['ContextReader (Effect event): first']); + + await act(() => root.render()); + assertLog(['ContextReader: second']); + + logContextValue(); + assertLog(['ContextReader (Effect event): first']); + }); }); From 6bec011b407fe8a2d4babb363289ccce4bc8fcf3 Mon Sep 17 00:00:00 2001 From: Sebastian Sebbie Silbermann Date: Mon, 13 Oct 2025 09:59:25 +0200 Subject: [PATCH 2/2] [Fiber] Ensure Effect events read latest values in `forwardRef` and `memo()` Components --- .../src/ReactFiberCommitWork.js | 8 ++-- .../src/__tests__/useEffectEvent-test.js | 39 +++++++++++++++++++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js index c98edc7a0bc..dbb4e7a7973 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.js @@ -493,7 +493,9 @@ function commitBeforeMutationEffectsOnFiber( } switch (finishedWork.tag) { - case FunctionComponent: { + case FunctionComponent: + case ForwardRef: + case SimpleMemoComponent: { if (enableUseEffectEventHook) { if ((flags & Update) !== NoFlags) { const updateQueue: FunctionComponentUpdateQueue | null = @@ -510,10 +512,6 @@ function commitBeforeMutationEffectsOnFiber( } break; } - case ForwardRef: - case SimpleMemoComponent: { - break; - } case ClassComponent: { if ((flags & Snapshot) !== NoFlags) { if (current !== null) { diff --git a/packages/react-reconciler/src/__tests__/useEffectEvent-test.js b/packages/react-reconciler/src/__tests__/useEffectEvent-test.js index edee01ef634..f263c9af269 100644 --- a/packages/react-reconciler/src/__tests__/useEffectEvent-test.js +++ b/packages/react-reconciler/src/__tests__/useEffectEvent-test.js @@ -887,6 +887,45 @@ describe('useEffectEvent', () => { assertLog(['ContextReader: second']); logContextValue(); + assertLog(['ContextReader (Effect event): second']); + }); + + it('reads the latest context value in forwardRef Components', async () => { + const MyContext = createContext('default'); + + let logContextValue; + const ContextReader = React.forwardRef(function ContextReader(props, ref) { + const value = useContext(MyContext); + Scheduler.log('ContextReader: ' + value); + const fireLogContextValue = useEffectEvent(() => { + Scheduler.log('ContextReader (Effect event): ' + value); + }); + useEffect(() => { + logContextValue = fireLogContextValue; + }, []); + return null; + }); + + function App({value}) { + return ( + + + + ); + } + + const root = ReactNoop.createRoot(); + await act(() => root.render()); + assertLog(['ContextReader: first']); + + logContextValue(); + assertLog(['ContextReader (Effect event): first']); + + await act(() => root.render()); + assertLog(['ContextReader: second']); + + logContextValue(); + assertLog(['ContextReader (Effect event): second']); }); });