diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index 7c659b7775a1e..68a811e3fe088 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -1142,6 +1142,16 @@ export function unbatchedUpdates(fn: (a: A) => R, a: A): R { export function flushSync(fn: A => R, a: A): R { const prevExecutionContext = executionContext; + if ((prevExecutionContext & (RenderContext | CommitContext)) !== NoContext) { + if (__DEV__) { + console.error( + 'flushSync was called from inside a lifecycle method. React cannot ' + + 'flush when React is already rendering. Consider moving this call to ' + + 'a scheduler task or micro task.', + ); + } + return fn(a); + } executionContext |= BatchedContext; const previousPriority = getCurrentUpdatePriority(); @@ -1158,17 +1168,7 @@ export function flushSync(fn: A => R, a: A): R { // Flush the immediate callbacks that were scheduled during this batch. // Note that this will happen even if batchedUpdates is higher up // the stack. - if ((executionContext & (RenderContext | CommitContext)) === NoContext) { - flushSyncCallbacks(); - } else { - if (__DEV__) { - console.error( - 'flushSync was called from inside a lifecycle method. React cannot ' + - 'flush when React is already rendering. Consider moving this call to ' + - 'a scheduler task or micro task.', - ); - } - } + flushSyncCallbacks(); } } diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index 530646f8a4d29..729e8e6717d05 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -1142,6 +1142,16 @@ export function unbatchedUpdates(fn: (a: A) => R, a: A): R { export function flushSync(fn: A => R, a: A): R { const prevExecutionContext = executionContext; + if ((prevExecutionContext & (RenderContext | CommitContext)) !== NoContext) { + if (__DEV__) { + console.error( + 'flushSync was called from inside a lifecycle method. React cannot ' + + 'flush when React is already rendering. Consider moving this call to ' + + 'a scheduler task or micro task.', + ); + } + return fn(a); + } executionContext |= BatchedContext; const previousPriority = getCurrentUpdatePriority(); @@ -1158,17 +1168,7 @@ export function flushSync(fn: A => R, a: A): R { // Flush the immediate callbacks that were scheduled during this batch. // Note that this will happen even if batchedUpdates is higher up // the stack. - if ((executionContext & (RenderContext | CommitContext)) === NoContext) { - flushSyncCallbacks(); - } else { - if (__DEV__) { - console.error( - 'flushSync was called from inside a lifecycle method. React cannot ' + - 'flush when React is already rendering. Consider moving this call to ' + - 'a scheduler task or micro task.', - ); - } - } + flushSyncCallbacks(); } } diff --git a/packages/react-reconciler/src/__tests__/ReactFlushSync-test.js b/packages/react-reconciler/src/__tests__/ReactFlushSync-test.js deleted file mode 100644 index f763e44705aaa..0000000000000 --- a/packages/react-reconciler/src/__tests__/ReactFlushSync-test.js +++ /dev/null @@ -1,64 +0,0 @@ -let React; -let ReactNoop; -let Scheduler; -let useState; -let useEffect; - -describe('ReactFlushSync', () => { - beforeEach(() => { - jest.resetModules(); - - React = require('react'); - ReactNoop = require('react-noop-renderer'); - Scheduler = require('scheduler'); - useState = React.useState; - useEffect = React.useEffect; - }); - - function Text({text}) { - Scheduler.unstable_yieldValue(text); - return text; - } - - // @gate experimental || !enableSyncDefaultUpdates - test('changes priority of updates in useEffect', async () => { - function App() { - const [syncState, setSyncState] = useState(0); - const [state, setState] = useState(0); - useEffect(() => { - if (syncState !== 1) { - setState(1); - ReactNoop.flushSync(() => setSyncState(1)); - } - }, [syncState, state]); - return ; - } - - const root = ReactNoop.createRoot(); - await ReactNoop.act(async () => { - if (gate(flags => flags.enableSyncDefaultUpdates)) { - React.unstable_startTransition(() => { - root.render(); - }); - } else { - root.render(); - } - // This will yield right before the passive effect fires - expect(Scheduler).toFlushUntilNextPaint(['0, 0']); - - // The passive effect will schedule a sync update and a normal update. - // They should commit in two separate batches. First the sync one. - expect(() => { - expect(Scheduler).toFlushUntilNextPaint(['1, 0']); - }).toErrorDev('flushSync was called from inside a lifecycle method'); - - // The remaining update is not sync - ReactNoop.flushSync(); - expect(Scheduler).toHaveYielded([]); - - // Now flush it. - expect(Scheduler).toFlushUntilNextPaint(['1, 1']); - }); - expect(root).toMatchRenderedOutput('1, 1'); - }); -});