diff --git a/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js b/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js
index 889b26387d8f9..77dd7e53355da 100644
--- a/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js
@@ -127,7 +127,7 @@ describe('ReactDOMFiberAsync', () => {
expect(ops).toEqual(['A', 'ABCD']);
});
- it('flushSync throws if already performing work', () => {
+ it('flushSync logs an error if already performing work', () => {
class Component extends React.Component {
componentDidUpdate() {
ReactDOM.flushSync(() => {});
@@ -140,7 +140,7 @@ describe('ReactDOMFiberAsync', () => {
// Initial mount
ReactDOM.render(, container);
// Update
- expect(() => ReactDOM.render(, container)).toThrow(
+ expect(() => ReactDOM.render(, container)).toErrorDev(
'flushSync was called from inside a lifecycle method',
);
});
diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js
index c4841ac63fa9c..2799e38237d58 100644
--- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js
@@ -1108,14 +1108,17 @@ export function unbatchedUpdates(fn: (a: A) => R, a: A): R {
}
export function flushSync(fn: A => R, a: A): R {
- if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
- invariant(
- false,
- 'flushSync was called from inside a lifecycle method. It cannot be ' +
- 'called when React is already rendering.',
- );
- }
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;
try {
return runWithPriority(ImmediatePriority, fn.bind(null, a));
diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js
index a771b96568d16..6ce1eca55bd96 100644
--- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js
@@ -1150,14 +1150,17 @@ export function unbatchedUpdates(fn: (a: A) => R, a: A): R {
}
export function flushSync(fn: A => R, a: A): R {
- if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
- invariant(
- false,
- 'flushSync was called from inside a lifecycle method. It cannot be ' +
- 'called when React is already rendering.',
- );
- }
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;
try {
return runWithPriority(ImmediatePriority, fn.bind(null, a));
diff --git a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
index 424dc60946ac5..13ea6cf0748ee 100644
--- a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
@@ -1808,22 +1808,26 @@ describe('ReactHooksWithNoopRenderer', () => {
ReactNoop.flushSync(() => {
updateCount(props.count);
});
+ // This shouldn't flush synchronously.
+ expect(ReactNoop.getChildren()).not.toEqual([
+ span('Count: ' + props.count),
+ ]);
}, [props.count]);
return ;
}
- act(() => {
- ReactNoop.render(, () =>
- Scheduler.unstable_yieldValue('Sync effect'),
- );
- expect(Scheduler).toFlushAndYieldThrough([
- 'Count: (empty)',
- 'Sync effect',
- ]);
- expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);
- expect(() => {
- ReactNoop.flushPassiveEffects();
- }).toThrow('flushSync was called from inside a lifecycle method');
- });
+ expect(() =>
+ act(() => {
+ ReactNoop.render(, () =>
+ Scheduler.unstable_yieldValue('Sync effect'),
+ );
+ expect(Scheduler).toFlushAndYieldThrough([
+ 'Count: (empty)',
+ 'Sync effect',
+ ]);
+ expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);
+ }),
+ ).toErrorDev('flushSync was called from inside a lifecycle method');
+ expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
});
it('unmounts previous effect', () => {