From cf8c068d580b02a12306909050c7883a18574ccb Mon Sep 17 00:00:00 2001 From: Rick Hanlon Date: Sun, 18 Jan 2026 16:38:49 -0500 Subject: [PATCH 1/2] [test] add activity test with gSBU and enableViewTransition bugfix --- .../src/__tests__/Activity-test.js | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/packages/react-reconciler/src/__tests__/Activity-test.js b/packages/react-reconciler/src/__tests__/Activity-test.js index 2fdcc16701d..44566ae3954 100644 --- a/packages/react-reconciler/src/__tests__/Activity-test.js +++ b/packages/react-reconciler/src/__tests__/Activity-test.js @@ -1549,6 +1549,86 @@ describe('Activity', () => { expect(root).toMatchRenderedOutput(); }); + // @gate enableActivity + it('getSnapshotBeforeUpdate does not run in hidden trees', async () => { + let setState; + + class Child extends React.Component { + getSnapshotBeforeUpdate(prevProps) { + const snapshot = `snapshot-${prevProps.value}-to-${this.props.value}`; + Scheduler.log(`getSnapshotBeforeUpdate: ${snapshot}`); + return snapshot; + } + componentDidUpdate(prevProps, prevState, snapshot) { + Scheduler.log(`componentDidUpdate: ${snapshot}`); + } + componentDidMount() { + Scheduler.log('componentDidMount'); + } + componentWillUnmount() { + Scheduler.log('componentWillUnmount'); + } + render() { + Scheduler.log(`render: ${this.props.value} `); + return ; + } + } + + function Wrapper({show}) { + const [value, _setState] = useState(1); + setState = _setState; + return ( + + + + ); + } + + const root = ReactNoop.createRoot(); + + // Initial render + await act(() => { + root.render(); + }); + assertLog(['render: 1', 'componentDidMount']); + + // Hide the Activity + await act(() => { + root.render(); + }); + assertLog([ + 'componentWillUnmount', + 'render: 1', + // Bugfix: snapshots for hidden trees should not need to be read. + ...(gate('enableViewTransition') + ? [] + : ['getSnapshotBeforeUpdate: snapshot-1-to-1']), + ]); + + // Trigger an update while hidden by calling setState + await act(() => { + setState(2); + }); + assertLog([ + 'render: 2', + ...(gate('enableViewTransition') + ? [] + : ['getSnapshotBeforeUpdate: snapshot-1-to-2']), + ]); + + // This is treated as a new mount so the snapshot also shouldn't be read. + await act(() => { + root.render(); + }); + assertLog([ + 'render: 2', + ...(gate('enableViewTransition') + ? [] + : ['getSnapshotBeforeUpdate: snapshot-2-to-2']), + 'componentDidMount', + ]); + }); + // @gate enableActivity it('warns if you pass a hidden prop', async () => { function App() { From e9100f924f9b1bb04ca454f1c428d63c55782c7e Mon Sep 17 00:00:00 2001 From: Rick Hanlon Date: Wed, 21 Jan 2026 10:34:58 -0500 Subject: [PATCH 2/2] Fix tests --- packages/react-reconciler/src/__tests__/Activity-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-reconciler/src/__tests__/Activity-test.js b/packages/react-reconciler/src/__tests__/Activity-test.js index 44566ae3954..388225d2029 100644 --- a/packages/react-reconciler/src/__tests__/Activity-test.js +++ b/packages/react-reconciler/src/__tests__/Activity-test.js @@ -1569,7 +1569,7 @@ describe('Activity', () => { Scheduler.log('componentWillUnmount'); } render() { - Scheduler.log(`render: ${this.props.value} `); + Scheduler.log(`render: ${this.props.value}`); return ; } }