@samitouri / QOS-React-2 / commits / 53ce0c3452

Allow flushSync to noop in life cycles but with a warning (#18759)

Sebastian Markbåge committed Apr 27, 2020 at 20:07 UTC 53ce0c34524349501cdadf4fea187671aba6a38d
4 files changed +39 -29
packages/react-dom/src/__tests__/ReactDOMFiberAsync-test.js
+2 -2
@@ -127,7 +127,7 @@ describe('ReactDOMFiberAsync', () => {
127 expect(ops).toEqual(['A', 'ABCD']);
128 });
129
130 - it('flushSync throws if already performing work', () => {
130 + it('flushSync logs an error if already performing work', () => {
131 class Component extends React.Component {
132 componentDidUpdate() {
133 ReactDOM.flushSync(() => {});
@@ -140,7 +140,7 @@ describe('ReactDOMFiberAsync', () => {
140 // Initial mount
141 ReactDOM.render(<Component />, container);
142 // Update
143 - expect(() => ReactDOM.render(<Component />, container)).toThrow(
143 + expect(() => ReactDOM.render(<Component />, container)).toErrorDev(
144 'flushSync was called from inside a lifecycle method',
145 );
146 });
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+10 -7
@@ -1228,14 +1228,17 @@ export function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {
1228 }
1229
1230 export function flushSync<A, R>(fn: A => R, a: A): R {
1231 - if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
1232 - invariant(
1233 - false,
1234 - 'flushSync was called from inside a lifecycle method. It cannot be ' +
1235 - 'called when React is already rendering.',
1236 - );
1237 - }
1231 const prevExecutionContext = executionContext;
1232 + if ((prevExecutionContext & (RenderContext | CommitContext)) !== NoContext) {
1233 + if (__DEV__) {
1234 + console.error(
1235 + 'flushSync was called from inside a lifecycle method. React cannot ' +
1236 + 'flush when React is already rendering. Consider moving this call to ' +
1237 + 'a scheduler task or micro task.',
1238 + );
1239 + }
1240 + return fn(a);
1241 + }
1242 executionContext |= BatchedContext;
1243 try {
1244 return runWithPriority(ImmediateSchedulerPriority, fn.bind(null, a));
packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+10 -7
@@ -1150,14 +1150,17 @@ export function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {
1150 }
1151
1152 export function flushSync<A, R>(fn: A => R, a: A): R {
1153 - if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
1154 - invariant(
1155 - false,
1156 - 'flushSync was called from inside a lifecycle method. It cannot be ' +
1157 - 'called when React is already rendering.',
1158 - );
1159 - }
1153 const prevExecutionContext = executionContext;
1154 + if ((prevExecutionContext & (RenderContext | CommitContext)) !== NoContext) {
1155 + if (__DEV__) {
1156 + console.error(
1157 + 'flushSync was called from inside a lifecycle method. React cannot ' +
1158 + 'flush when React is already rendering. Consider moving this call to ' +
1159 + 'a scheduler task or micro task.',
1160 + );
1161 + }
1162 + return fn(a);
1163 + }
1164 executionContext |= BatchedContext;
1165 try {
1166 return runWithPriority(ImmediatePriority, fn.bind(null, a));
packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
+17 -13
@@ -1808,22 +1808,26 @@ describe('ReactHooksWithNoopRenderer', () => {
1808 ReactNoop.flushSync(() => {
1809 updateCount(props.count);
1810 });
1811 + // This shouldn't flush synchronously.
1812 + expect(ReactNoop.getChildren()).not.toEqual([
1813 + span('Count: ' + props.count),
1814 + ]);
1815 }, [props.count]);
1816 return <Text text={'Count: ' + count} />;
1817 }
1814 - act(() => {
1815 - ReactNoop.render(<Counter count={0} />, () =>
1816 - Scheduler.unstable_yieldValue('Sync effect'),
1817 - );
1818 - expect(Scheduler).toFlushAndYieldThrough([
1819 - 'Count: (empty)',
1820 - 'Sync effect',
1821 - ]);
1822 - expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);
1823 - expect(() => {
1824 - ReactNoop.flushPassiveEffects();
1825 - }).toThrow('flushSync was called from inside a lifecycle method');
1826 - });
1818 + expect(() =>
1819 + act(() => {
1820 + ReactNoop.render(<Counter count={0} />, () =>
1821 + Scheduler.unstable_yieldValue('Sync effect'),
1822 + );
1823 + expect(Scheduler).toFlushAndYieldThrough([
1824 + 'Count: (empty)',
1825 + 'Sync effect',
1826 + ]);
1827 + expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);
1828 + }),
1829 + ).toErrorDev('flushSync was called from inside a lifecycle method');
1830 + expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1831 });
1832
1833 it('unmounts previous effect', () => {