From 23fcd7cea1ec553f9b217bc148e8d94e2d0097bd Mon Sep 17 00:00:00 2001 From: "Sebastian \"Sebbie\" Silbermann" Date: Fri, 17 Apr 2026 18:01:56 +0200 Subject: [PATCH 1/6] Minify prod error messages for all browser bundles (#36277) --- scripts/error-codes/codes.json | 18 ++++++++++++++++-- scripts/rollup/bundles.js | 16 ++++++++-------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index db92efc075c..345aa2b0aaf 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -568,5 +568,19 @@ "580": "Server Function has too many bound arguments. Received %s but the limit is %s.", "581": "BigInt is too large. Received %s digits but the limit is %s.", "582": "Referenced Blob is not a Blob.", - "583": "The current renderer does not support view transitions. This error is likely caused by a bug in React. Please file an issue." -} + "583": "The current renderer does not support view transitions. This error is likely caused by a bug in React. Please file an issue.", + "584": "Attempted to load a Server Reference outside the hosted root.", + "585": "Invalid server action: %s", + "586": "No server callback has been registered. Call setServerCallback to register one.", + "587": "Server actions must be functions", + "588": "Could not find the module \"%s\" in the React Server Consumer Manifest. This is probably a bug in the React Server Components bundler.", + "589": "Could not find the module \"%s\" in the React Server Manifest. This is probably a bug in the React Server Components bundler.", + "590": "Cannot await or return from a thenable. You cannot await a client module from a server component.", + "591": "Cannot access %s on the server. You cannot dot into a client module from a server component. You can only pass the imported name through.", + "592": "Cannot assign to a client module from a server module.", + "593": "Attempted to call the default export of %s from the server but it's on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.", + "594": "Cannot read Symbol exports. Only named exports are supported on a client module imported on the server.", + "595": "Attempted to call %s() from the server but %s is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.", + "596": "Could not find the module \"%s\" in the React Client Manifest. This is probably a bug in the React Server Components bundler.", + "597": "The module \"%s\" is marked as an async ESM module but was loaded as a CJS proxy. This is probably a bug in the React Server Components bundler." +} \ No newline at end of file diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js index 7b9508b9e29..25e5f31bf9e 100644 --- a/scripts/rollup/bundles.js +++ b/scripts/rollup/bundles.js @@ -415,7 +415,7 @@ const bundles = [ entry: 'react-dom/unstable_server-external-runtime', outputPath: 'unstable_server-external-runtime.js', global: 'ReactDOMServerExternalRuntime', - minifyWithProdErrorCodes: false, + minifyWithProdErrorCodes: true, wrapWithModuleBoundaries: false, externals: [], }, @@ -454,7 +454,7 @@ const bundles = [ name: 'react-server-dom-webpack-server.browser', condition: 'react-server', global: 'ReactServerDOMServer', - minifyWithProdErrorCodes: false, + minifyWithProdErrorCodes: true, wrapWithModuleBoundaries: false, externals: ['react', 'react-dom'], }, @@ -496,7 +496,7 @@ const bundles = [ 'react-server-dom-webpack/src/client/react-flight-dom-client.browser', name: 'react-server-dom-webpack-client.browser', global: 'ReactServerDOMClient', - minifyWithProdErrorCodes: false, + minifyWithProdErrorCodes: true, wrapWithModuleBoundaries: false, externals: ['react', 'react-dom'], }, @@ -566,7 +566,7 @@ const bundles = [ name: 'react-server-dom-turbopack-server.browser', condition: 'react-server', global: 'ReactServerDOMServer', - minifyWithProdErrorCodes: false, + minifyWithProdErrorCodes: true, wrapWithModuleBoundaries: false, externals: ['react', 'react-dom'], }, @@ -608,7 +608,7 @@ const bundles = [ 'react-server-dom-turbopack/src/client/react-flight-dom-client.browser', name: 'react-server-dom-turbopack-client.browser', global: 'ReactServerDOMClient', - minifyWithProdErrorCodes: false, + minifyWithProdErrorCodes: true, wrapWithModuleBoundaries: false, externals: ['react', 'react-dom'], }, @@ -641,7 +641,7 @@ const bundles = [ name: 'react-server-dom-parcel-server.browser', condition: 'react-server', global: 'ReactServerDOMServer', - minifyWithProdErrorCodes: false, + minifyWithProdErrorCodes: true, wrapWithModuleBoundaries: false, externals: ['react', 'react-dom'], }, @@ -682,7 +682,7 @@ const bundles = [ entry: 'react-server-dom-parcel/src/client/react-flight-dom-client.browser', name: 'react-server-dom-parcel-client.browser', global: 'ReactServerDOMClient', - minifyWithProdErrorCodes: false, + minifyWithProdErrorCodes: true, wrapWithModuleBoundaries: false, externals: ['react', 'react-dom'], }, @@ -731,7 +731,7 @@ const bundles = [ bundleTypes: [NODE_DEV, NODE_PROD, ESM_DEV, ESM_PROD], moduleType: RENDERER, entry: 'react-server-dom-esm/client.browser', - minifyWithProdErrorCodes: false, + minifyWithProdErrorCodes: true, wrapWithModuleBoundaries: false, externals: ['react', 'react-dom'], }, From 67e47593b607ecc08ac59361d8aba7ad2eef028a Mon Sep 17 00:00:00 2001 From: "Sebastian \"Sebbie\" Silbermann" Date: Fri, 17 Apr 2026 18:09:37 +0200 Subject: [PATCH 2/6] [Fiber] Double invoke Effects in Strict Mode during Hydration (#35961) --- ...DOMServerPartialHydration-test.internal.js | 73 +++++++++++ .../ReactServerRenderingHydration-test.js | 50 +++++++ .../src/ReactFiberBeginWork.js | 10 +- .../src/ReactFiberWorkLoop.js | 6 +- .../src/__tests__/ActivityStrictMode-test.js | 123 ++++++++++++++++++ 5 files changed, 257 insertions(+), 5 deletions(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js index 3e6af7bc974..2572d7043ca 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js @@ -20,6 +20,7 @@ let Scheduler; let Suspense; let SuspenseList; let useSyncExternalStore; +let use; let act; let IdleEventPriority; let waitForAll; @@ -116,6 +117,7 @@ describe('ReactDOMServerPartialHydration', () => { Activity = React.Activity; Suspense = React.Suspense; useSyncExternalStore = React.useSyncExternalStore; + use = React.use; if (gate(flags => flags.enableSuspenseList)) { SuspenseList = React.unstable_SuspenseList; } @@ -256,6 +258,77 @@ describe('ReactDOMServerPartialHydration', () => { expect(container.textContent).toBe('HelloHello'); }); + it('replays effects when a suspended boundary hydrates in StrictMode', async () => { + const log = []; + let suspend = false; + let resolve; + const promise = new Promise(resolvePromise => (resolve = resolvePromise)); + + function EffectfulChild() { + React.useLayoutEffect(() => { + log.push('layout mount'); + return () => log.push('layout unmount'); + }, []); + React.useEffect(() => { + log.push('effect mount'); + return () => log.push('effect unmount'); + }, []); + return 'Hello'; + } + + function Child() { + if (suspend) { + use(promise); + } + return ; + } + + function App() { + return ( + + + + ); + } + + const element = ( + + + + ); + + suspend = false; + const finalHTML = ReactDOMServer.renderToString(element); + const container = document.createElement('div'); + container.innerHTML = finalHTML; + expect(container.textContent).toBe('Hello'); + + suspend = true; + ReactDOMClient.hydrateRoot(container, element); + await waitForAll([]); + expect(log).toEqual([]); + expect(container.textContent).toBe('Hello'); + + suspend = false; + resolve(); + await promise; + await waitForAll([]); + + expect(container.textContent).toBe('Hello'); + if (__DEV__) { + expect(log).toEqual([ + 'layout mount', + 'effect mount', + 'layout unmount', + 'effect unmount', + 'layout mount', + 'effect mount', + ]); + } else { + expect(log).toEqual(['layout mount', 'effect mount']); + } + }); + it('falls back to client rendering boundary on mismatch', async () => { let client = false; let suspend = false; diff --git a/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js b/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js index 5675c7eb0e7..ccfda03e1bc 100644 --- a/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js +++ b/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js @@ -392,6 +392,56 @@ describe('ReactDOMServerHydration', () => { expect(element.textContent).toBe('Hi'); }); + it('replays effects when hydrating a StrictMode subtree', async () => { + const log = []; + function Child() { + React.useLayoutEffect(() => { + log.push('layout mount'); + return () => log.push('layout unmount'); + }, []); + React.useEffect(() => { + log.push('effect mount'); + return () => log.push('effect unmount'); + }, []); + return Hello; + } + + function App() { + return ( +
+ +
+ ); + } + + const markup = ( + + + + ); + + const element = document.createElement('div'); + element.innerHTML = ReactDOMServer.renderToString(markup); + expect(element.textContent).toBe('Hello'); + + await act(() => { + ReactDOMClient.hydrateRoot(element, markup); + }); + + if (__DEV__) { + expect(log).toEqual([ + 'layout mount', + 'effect mount', + 'layout unmount', + 'effect unmount', + 'layout mount', + 'effect mount', + ]); + } else { + expect(log).toEqual(['layout mount', 'effect mount']); + } + }); + it('should be able to render and hydrate forwardRef components', async () => { const FunctionComponent = ({label, forwardedRef}) => (
{label}
diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index cf2c0823673..b8709c03a42 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -88,6 +88,7 @@ import { NoFlags, PerformedWork, Placement, + PlacementDEV, Hydrating, Callback, ContentReset, @@ -1080,7 +1081,8 @@ function updateDehydratedActivityComponent( // Conceptually this is similar to Placement in that a new subtree is // inserted into the React tree here. It just happens to not need DOM // mutations because it already exists. - primaryChildFragment.flags |= Hydrating; + // We should still treat it as a newly inserted Fiber to double invoke Strict Effects. + primaryChildFragment.flags |= Hydrating | PlacementDEV; return primaryChildFragment; } } else { @@ -1899,7 +1901,8 @@ function updateHostRoot( // Conceptually this is similar to Placement in that a new subtree is // inserted into the React tree here. It just happens to not need DOM // mutations because it already exists. - node.flags = (node.flags & ~Placement) | Hydrating; + // We should still treat it as a newly inserted Fiber to double invoke Strict Effects. + node.flags = (node.flags & ~Placement) | Hydrating | PlacementDEV; node = node.sibling; } } @@ -3104,7 +3107,8 @@ function updateDehydratedSuspenseComponent( // Conceptually this is similar to Placement in that a new subtree is // inserted into the React tree here. It just happens to not need DOM // mutations because it already exists. - primaryChildFragment.flags |= Hydrating; + // We should still treat it as a newly inserted Fiber to double invoke Strict Effects. + primaryChildFragment.flags |= Hydrating | PlacementDEV; return primaryChildFragment; } } else { diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index d1153a201df..17d5e311c53 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -5312,9 +5312,11 @@ function doubleInvokeEffectsInDEVIfNecessary( if (fiber.memoizedState === null) { // Only consider Offscreen that is visible. // TODO (Offscreen) Handle manual mode. - if (isInStrictMode && fiber.flags & Visibility) { - // Double invoke effects on Offscreen's subtree only + if (isInStrictMode && fiber.flags & (Visibility | PlacementDEV)) { + // Double invoke effects on Offscreen's subtree // if it is visible and its visibility has changed. + // However, we also need to consider newly hydrated Offscreen because their + // visibility flags might not have changed. runWithFiberInDEV(fiber, doubleInvokeEffectsOnFiber, root, fiber); } else if (fiber.subtreeFlags & PlacementDEV) { // Something in the subtree could have been suspended. diff --git a/packages/react-reconciler/src/__tests__/ActivityStrictMode-test.js b/packages/react-reconciler/src/__tests__/ActivityStrictMode-test.js index fa67df3b8a1..887ac7d1ce8 100644 --- a/packages/react-reconciler/src/__tests__/ActivityStrictMode-test.js +++ b/packages/react-reconciler/src/__tests__/ActivityStrictMode-test.js @@ -230,4 +230,127 @@ describe('Activity StrictMode', () => { 'Child mount', ]); }); + + // @gate __DEV__ + it('should double invoke effects on newly inserted children while Activity becomes visible', async () => { + function Parent({children}) { + log.push('Parent rendered'); + React.useEffect(() => { + log.push('Parent mount'); + return () => { + log.push('Parent unmount'); + }; + }); + + return
{children}
; + } + + function Child({name}) { + log.push(`Child ${name} rendered`); + React.useEffect(() => { + log.push(`Child ${name} mount`); + return () => { + log.push(`Child ${name} unmount`); + }; + }); + + return null; + } + + await act(() => { + ReactNoop.render( + + + + + , + ); + }); + + expect(log).toEqual(['Parent rendered', 'Parent rendered']); + + log.length = 0; + await act(() => { + ReactNoop.render( + + + + + + + , + ); + }); + + expect(log).toEqual([ + 'Parent rendered', + 'Parent rendered', + 'Child one rendered', + 'Child one rendered', + 'Child one mount', + 'Parent mount', + // StrictMode double invocation + 'Parent unmount', + 'Child one unmount', + 'Child one mount', + 'Parent mount', + ]); + + log.length = 0; + await act(() => { + ReactNoop.render( + + + + + + + , + ); + }); + + expect(log).toEqual([ + 'Parent rendered', + 'Parent rendered', + 'Child one rendered', + 'Child one rendered', + // single Effect invocation. No double invocation on update. + 'Child one unmount', + 'Parent unmount', + 'Child one mount', + 'Parent mount', + ]); + + log.length = 0; + await act(() => { + ReactNoop.render( + + + + + + + + , + ); + }); + + expect(log).toEqual([ + 'Parent rendered', + 'Parent rendered', + 'Child one rendered', + 'Child one rendered', + 'Child two rendered', + 'Child two rendered', + // single Effect invocation for existing Components. + 'Child one unmount', + 'Parent unmount', + 'Child one mount', + 'Child two mount', + 'Parent mount', + // Double Effect invocation for new Component "two" + 'Child two unmount', + 'Child two mount', + ]); + }); }); From da9325b519376e2d65cdf6d509ade053e14ec5b3 Mon Sep 17 00:00:00 2001 From: "Sebastian \"Sebbie\" Silbermann" Date: Fri, 17 Apr 2026 18:14:50 +0200 Subject: [PATCH 3/6] [Fiber] Double invoke Effects in StrictMode after Fast Refresh (#35962) --- .../src/ReactFiberBeginWork.js | 2 +- .../src/__tests__/ReactFresh-test.js | 92 +++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index b8709c03a42..4f41a70e56e 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -3866,7 +3866,7 @@ function remountFiber( deletions.push(current); } - newWorkInProgress.flags |= Placement; + newWorkInProgress.flags |= Placement | PlacementDEV; // Restart work from the new fiber. return newWorkInProgress; diff --git a/packages/react-refresh/src/__tests__/ReactFresh-test.js b/packages/react-refresh/src/__tests__/ReactFresh-test.js index 6fb00a66a24..e0f2d490335 100644 --- a/packages/react-refresh/src/__tests__/ReactFresh-test.js +++ b/packages/react-refresh/src/__tests__/ReactFresh-test.js @@ -2326,6 +2326,98 @@ describe('ReactFresh', () => { expect(finalEl.style.color).toBe('orange'); } + it('double invokes effects after a forced remount in StrictMode', async () => { + if (__DEV__) { + const log = []; + + const createAppV1 = () => { + function Hello() { + React.useEffect(() => { + log.push('mount v1'); + return () => log.push('unmount v1'); + }, []); + return

Hello

; + } + $RefreshReg$(Hello, 'Hello'); + $RefreshSig$(Hello, '1'); + + return Hello; + }; + + const App = createAppV1(); + + await act(() => { + root.render( + + + , + ); + }); + + expect(log).toEqual(['mount v1', 'unmount v1', 'mount v1']); + log.length = 0; + + await patch(() => { + function Hello() { + React.useEffect(() => { + log.push('mount v2'); + return () => log.push('unmount v2'); + }, []); + return

Hello

; + } + $RefreshReg$(Hello, 'Hello'); + $RefreshSig$(Hello, '2'); + return null; + }); + + expect(container.firstChild.style.color).toBe('red'); + expect(log).toEqual(['unmount v1', 'mount v2', 'unmount v2', 'mount v2']); + } + }); + + it('double invokes an effect added during Fast Refresh remount in StrictMode', async () => { + if (__DEV__) { + const log = []; + + const createAppV1 = () => { + function Hello() { + return

Hello

; + } + $RefreshReg$(Hello, 'Hello'); + $RefreshSig$(Hello, '1'); + return Hello; + }; + + const App = createAppV1(); + + await act(() => { + root.render( + + + , + ); + }); + + expect(log).toEqual([]); + + await patch(() => { + function Hello() { + React.useEffect(() => { + log.push('mount v2'); + return () => log.push('unmount v2'); + }, []); + return

Hello

; + } + $RefreshReg$(Hello, 'Hello'); + $RefreshSig$(Hello, '2'); + return null; + }); + + expect(container.firstChild.style.color).toBe('red'); + expect(log).toEqual(['mount v2', 'unmount v2', 'mount v2']); + } + }); + it('resets hooks with dependencies on hot reload', async () => { if (__DEV__) { let useEffectWithEmptyArrayCalls = 0; From bc249804d3c2ea6ee95bc5543c3735a65d1239b5 Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Fri, 17 Apr 2026 12:31:24 -0400 Subject: [PATCH 4/6] [eprh] Add back a no-op for removed component-hook-factories rule (#36307) The `component-hook-factories` rule was removed in #35825 as part of a feature flag cleanup, but was listed in the README as part of the manual config example. This broke users who used a manual config (copied from the old README) in eslint-plugin-react-hooks 7.1.0. This adds back a deprecated no-op rule as a fix. #35825 removed other rules (`automatic-effect-dependencies` and `fire`), but these were for experimental features that did not ship. These were also not referenced in the README. --- packages/eslint-plugin-react-hooks/README.md | 2 -- packages/eslint-plugin-react-hooks/src/index.ts | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin-react-hooks/README.md b/packages/eslint-plugin-react-hooks/README.md index a1b4bcabb59..b0a4491c99e 100644 --- a/packages/eslint-plugin-react-hooks/README.md +++ b/packages/eslint-plugin-react-hooks/README.md @@ -74,7 +74,6 @@ export default [ // React Compiler rules 'react-hooks/config': 'error', 'react-hooks/error-boundaries': 'error', - 'react-hooks/component-hook-factories': 'error', 'react-hooks/gating': 'error', 'react-hooks/globals': 'error', 'react-hooks/immutability': 'error', @@ -108,7 +107,6 @@ export default [ // React Compiler rules "react-hooks/config": "error", "react-hooks/error-boundaries": "error", - "react-hooks/component-hook-factories": "error", "react-hooks/gating": "error", "react-hooks/globals": "error", "react-hooks/immutability": "error", diff --git a/packages/eslint-plugin-react-hooks/src/index.ts b/packages/eslint-plugin-react-hooks/src/index.ts index 924299d8989..33ee94a96e9 100644 --- a/packages/eslint-plugin-react-hooks/src/index.ts +++ b/packages/eslint-plugin-react-hooks/src/index.ts @@ -15,12 +15,29 @@ import { } from './shared/ReactCompiler'; import RulesOfHooks from './rules/RulesOfHooks'; +function makeDeprecatedRule(version: string): Rule.RuleModule { + return { + meta: { + type: 'suggestion', + docs: { + description: `Deprecated: this rule has been removed in ${version}.`, + }, + schema: [], + deprecated: true, + }, + create() { + return {}; + }, + }; +} + const rules = { 'exhaustive-deps': ExhaustiveDeps, 'rules-of-hooks': RulesOfHooks, ...Object.fromEntries( Object.entries(allRules).map(([name, config]) => [name, config.rule]), ), + 'component-hook-factories': makeDeprecatedRule('7.1.0'), } satisfies Record; const basicRuleConfigs = { From d1727fbf987392749b374f3afe62227d2c70fc41 Mon Sep 17 00:00:00 2001 From: mofeiZ <34200447+mofeiZ@users.noreply.github.com> Date: Fri, 17 Apr 2026 12:43:35 -0400 Subject: [PATCH 5/6] [eprh] Update changelog for 7.1.1 (#36308) --- ReactVersions.js | 2 +- packages/eslint-plugin-react-hooks/CHANGELOG.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ReactVersions.js b/ReactVersions.js index dd8c78afb13..a486ccf2d91 100644 --- a/ReactVersions.js +++ b/ReactVersions.js @@ -33,7 +33,7 @@ const canaryChannelLabel = 'canary'; const rcNumber = 0; const stablePackages = { - 'eslint-plugin-react-hooks': '7.1.0', + 'eslint-plugin-react-hooks': '7.1.1', 'jest-react': '0.18.0', react: ReactVersion, 'react-art': ReactVersion, diff --git a/packages/eslint-plugin-react-hooks/CHANGELOG.md b/packages/eslint-plugin-react-hooks/CHANGELOG.md index 4ad4bb411c7..fde29f76308 100644 --- a/packages/eslint-plugin-react-hooks/CHANGELOG.md +++ b/packages/eslint-plugin-react-hooks/CHANGELOG.md @@ -1,3 +1,9 @@ +## 7.1.1 + +**Note:** 7.1.0 accidentally removed the `component-hook-factories` rule, causing errors for users who referenced it in their ESLint config. This is now fixed. + +- Add deprecated no-op `component-hook-factories` rule for backwards compatibility. ([@mofeiZ](https://github.com/mofeiZ) in [#36307](https://github.com/facebook/react/pull/36307)) + ## 7.1.0 This release adds ESLint v10 support, improves performance by skipping compilation for non-React files, and includes compiler lint improvements including better `set-state-in-effect` detection, improved ref validation, and more helpful error reporting. From 1ddff43c41147b880c22eb363e07aade5a71c5d9 Mon Sep 17 00:00:00 2001 From: Zeya Peng Date: Fri, 17 Apr 2026 13:21:41 -0400 Subject: [PATCH 6/6] Add null check before calling fabricSuspendOnActiveViewTransition (#36310) ## Summary - Adds a null check before calling `fabricSuspendOnActiveViewTransition()` in the Fabric renderer's `suspendOnActiveViewTransition` export - Prevents crashes on hosts where `nativeFabricUIManager` does not yet implement `suspendOnActiveViewTransition` ## Test plan - Verified the change compiles correctly - Hosts with `suspendOnActiveViewTransition` implemented continue to work as before - Hosts without `suspendOnActiveViewTransition` no longer crash when view transitions are active --- packages/react-native-renderer/src/ReactFiberConfigFabric.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/react-native-renderer/src/ReactFiberConfigFabric.js b/packages/react-native-renderer/src/ReactFiberConfigFabric.js index fe3536c2050..29beb80db91 100644 --- a/packages/react-native-renderer/src/ReactFiberConfigFabric.js +++ b/packages/react-native-renderer/src/ReactFiberConfigFabric.js @@ -657,7 +657,9 @@ export function suspendOnActiveViewTransition( state: SuspendedState, container: Container, ): void { - fabricSuspendOnActiveViewTransition(); + if (fabricSuspendOnActiveViewTransition != null) { + fabricSuspendOnActiveViewTransition(); + } } export function waitForCommitToBeReady(