1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @emails react-core
    
  8.  * @jest-environment node
    
  9.  */
    
  10. 
    
  11. /* eslint-disable no-func-assign */
    
  12. 
    
  13. 'use strict';
    
  14. 
    
  15. let React;
    
  16. let ReactFeatureFlags;
    
  17. let ReactTestRenderer;
    
  18. let Scheduler;
    
  19. let ReactDOMServer;
    
  20. let act;
    
  21. let assertLog;
    
  22. let waitForAll;
    
  23. let waitForThrow;
    
  24. 
    
  25. describe('ReactHooks', () => {
    
  26.   beforeEach(() => {
    
  27.     jest.resetModules();
    
  28. 
    
  29.     ReactFeatureFlags = require('shared/ReactFeatureFlags');
    
  30. 
    
  31.     React = require('react');
    
  32.     ReactTestRenderer = require('react-test-renderer');
    
  33.     Scheduler = require('scheduler');
    
  34.     ReactDOMServer = require('react-dom/server');
    
  35.     act = require('internal-test-utils').act;
    
  36. 
    
  37.     const InternalTestUtils = require('internal-test-utils');
    
  38.     assertLog = InternalTestUtils.assertLog;
    
  39.     waitForAll = InternalTestUtils.waitForAll;
    
  40.     waitForThrow = InternalTestUtils.waitForThrow;
    
  41.   });
    
  42. 
    
  43.   if (__DEV__) {
    
  44.     // useDebugValue is a DEV-only hook
    
  45.     it('useDebugValue throws when used in a class component', () => {
    
  46.       class Example extends React.Component {
    
  47.         render() {
    
  48.           React.useDebugValue('abc');
    
  49.           return null;
    
  50.         }
    
  51.       }
    
  52.       expect(() => {
    
  53.         ReactTestRenderer.create(<Example />);
    
  54.       }).toThrow(
    
  55.         'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen' +
    
  56.           ' for one of the following reasons:\n' +
    
  57.           '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
    
  58.           '2. You might be breaking the Rules of Hooks\n' +
    
  59.           '3. You might have more than one copy of React in the same app\n' +
    
  60.           'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.',
    
  61.       );
    
  62.     });
    
  63.   }
    
  64. 
    
  65.   it('bails out in the render phase if all of the state is the same', async () => {
    
  66.     const {useState, useLayoutEffect} = React;
    
  67. 
    
  68.     function Child({text}) {
    
  69.       Scheduler.log('Child: ' + text);
    
  70.       return text;
    
  71.     }
    
  72. 
    
  73.     let setCounter1;
    
  74.     let setCounter2;
    
  75.     function Parent() {
    
  76.       const [counter1, _setCounter1] = useState(0);
    
  77.       setCounter1 = _setCounter1;
    
  78.       const [counter2, _setCounter2] = useState(0);
    
  79.       setCounter2 = _setCounter2;
    
  80. 
    
  81.       const text = `${counter1}, ${counter2}`;
    
  82.       Scheduler.log(`Parent: ${text}`);
    
  83.       useLayoutEffect(() => {
    
  84.         Scheduler.log(`Effect: ${text}`);
    
  85.       });
    
  86.       return <Child text={text} />;
    
  87.     }
    
  88. 
    
  89.     const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
    
  90.     root.update(<Parent />);
    
  91.     await waitForAll(['Parent: 0, 0', 'Child: 0, 0', 'Effect: 0, 0']);
    
  92.     expect(root).toMatchRenderedOutput('0, 0');
    
  93. 
    
  94.     // Normal update
    
  95.     await act(() => {
    
  96.       setCounter1(1);
    
  97.       setCounter2(1);
    
  98.     });
    
  99. 
    
  100.     assertLog(['Parent: 1, 1', 'Child: 1, 1', 'Effect: 1, 1']);
    
  101. 
    
  102.     // Update that bails out.
    
  103.     await act(() => setCounter1(1));
    
  104.     assertLog(['Parent: 1, 1']);
    
  105. 
    
  106.     // This time, one of the state updates but the other one doesn't. So we
    
  107.     // can't bail out.
    
  108.     await act(() => {
    
  109.       setCounter1(1);
    
  110.       setCounter2(2);
    
  111.     });
    
  112. 
    
  113.     assertLog(['Parent: 1, 2', 'Child: 1, 2', 'Effect: 1, 2']);
    
  114. 
    
  115.     // Lots of updates that eventually resolve to the current values.
    
  116.     await act(() => {
    
  117.       setCounter1(9);
    
  118.       setCounter2(3);
    
  119.       setCounter1(4);
    
  120.       setCounter2(7);
    
  121.       setCounter1(1);
    
  122.       setCounter2(2);
    
  123.     });
    
  124. 
    
  125.     // Because the final values are the same as the current values, the
    
  126.     // component bails out.
    
  127.     assertLog(['Parent: 1, 2']);
    
  128. 
    
  129.     // prepare to check SameValue
    
  130.     await act(() => {
    
  131.       setCounter1(0 / -1);
    
  132.       setCounter2(NaN);
    
  133.     });
    
  134. 
    
  135.     assertLog(['Parent: 0, NaN', 'Child: 0, NaN', 'Effect: 0, NaN']);
    
  136. 
    
  137.     // check if re-setting to negative 0 / NaN still bails out
    
  138.     await act(() => {
    
  139.       setCounter1(0 / -1);
    
  140.       setCounter2(NaN);
    
  141.       setCounter2(Infinity);
    
  142.       setCounter2(NaN);
    
  143.     });
    
  144. 
    
  145.     assertLog(['Parent: 0, NaN']);
    
  146. 
    
  147.     // check if changing negative 0 to positive 0 does not bail out
    
  148.     await act(() => {
    
  149.       setCounter1(0);
    
  150.     });
    
  151.     assertLog(['Parent: 0, NaN', 'Child: 0, NaN', 'Effect: 0, NaN']);
    
  152.   });
    
  153. 
    
  154.   it('bails out in render phase if all the state is the same and props bail out with memo', async () => {
    
  155.     const {useState, memo} = React;
    
  156. 
    
  157.     function Child({text}) {
    
  158.       Scheduler.log('Child: ' + text);
    
  159.       return text;
    
  160.     }
    
  161. 
    
  162.     let setCounter1;
    
  163.     let setCounter2;
    
  164.     function Parent({theme}) {
    
  165.       const [counter1, _setCounter1] = useState(0);
    
  166.       setCounter1 = _setCounter1;
    
  167.       const [counter2, _setCounter2] = useState(0);
    
  168.       setCounter2 = _setCounter2;
    
  169. 
    
  170.       const text = `${counter1}, ${counter2} (${theme})`;
    
  171.       Scheduler.log(`Parent: ${text}`);
    
  172.       return <Child text={text} />;
    
  173.     }
    
  174. 
    
  175.     Parent = memo(Parent);
    
  176. 
    
  177.     const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
    
  178.     root.update(<Parent theme="light" />);
    
  179.     await waitForAll(['Parent: 0, 0 (light)', 'Child: 0, 0 (light)']);
    
  180.     expect(root).toMatchRenderedOutput('0, 0 (light)');
    
  181. 
    
  182.     // Normal update
    
  183.     await act(() => {
    
  184.       setCounter1(1);
    
  185.       setCounter2(1);
    
  186.     });
    
  187. 
    
  188.     assertLog(['Parent: 1, 1 (light)', 'Child: 1, 1 (light)']);
    
  189. 
    
  190.     // Update that bails out.
    
  191.     await act(() => setCounter1(1));
    
  192.     assertLog(['Parent: 1, 1 (light)']);
    
  193. 
    
  194.     // This time, one of the state updates but the other one doesn't. So we
    
  195.     // can't bail out.
    
  196.     await act(() => {
    
  197.       setCounter1(1);
    
  198.       setCounter2(2);
    
  199.     });
    
  200. 
    
  201.     assertLog(['Parent: 1, 2 (light)', 'Child: 1, 2 (light)']);
    
  202. 
    
  203.     // Updates bail out, but component still renders because props
    
  204.     // have changed
    
  205.     await act(() => {
    
  206.       setCounter1(1);
    
  207.       setCounter2(2);
    
  208.       root.update(<Parent theme="dark" />);
    
  209.     });
    
  210. 
    
  211.     assertLog(['Parent: 1, 2 (dark)', 'Child: 1, 2 (dark)']);
    
  212. 
    
  213.     // Both props and state bail out
    
  214.     await act(() => {
    
  215.       setCounter1(1);
    
  216.       setCounter2(2);
    
  217.       root.update(<Parent theme="dark" />);
    
  218.     });
    
  219. 
    
  220.     assertLog(['Parent: 1, 2 (dark)']);
    
  221.   });
    
  222. 
    
  223.   it('warns about setState second argument', async () => {
    
  224.     const {useState} = React;
    
  225. 
    
  226.     let setCounter;
    
  227.     function Counter() {
    
  228.       const [counter, _setCounter] = useState(0);
    
  229.       setCounter = _setCounter;
    
  230. 
    
  231.       Scheduler.log(`Count: ${counter}`);
    
  232.       return counter;
    
  233.     }
    
  234. 
    
  235.     const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
    
  236.     root.update(<Counter />);
    
  237.     await waitForAll(['Count: 0']);
    
  238.     expect(root).toMatchRenderedOutput('0');
    
  239. 
    
  240.     await expect(async () => {
    
  241.       await act(() =>
    
  242.         setCounter(1, () => {
    
  243.           throw new Error('Expected to ignore the callback.');
    
  244.         }),
    
  245.       );
    
  246.     }).toErrorDev(
    
  247.       'State updates from the useState() and useReducer() Hooks ' +
    
  248.         "don't support the second callback argument. " +
    
  249.         'To execute a side effect after rendering, ' +
    
  250.         'declare it in the component body with useEffect().',
    
  251.       {withoutStack: true},
    
  252.     );
    
  253.     assertLog(['Count: 1']);
    
  254.     expect(root).toMatchRenderedOutput('1');
    
  255.   });
    
  256. 
    
  257.   it('warns about dispatch second argument', async () => {
    
  258.     const {useReducer} = React;
    
  259. 
    
  260.     let dispatch;
    
  261.     function Counter() {
    
  262.       const [counter, _dispatch] = useReducer((s, a) => a, 0);
    
  263.       dispatch = _dispatch;
    
  264. 
    
  265.       Scheduler.log(`Count: ${counter}`);
    
  266.       return counter;
    
  267.     }
    
  268. 
    
  269.     const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
    
  270.     root.update(<Counter />);
    
  271.     await waitForAll(['Count: 0']);
    
  272.     expect(root).toMatchRenderedOutput('0');
    
  273. 
    
  274.     await expect(async () => {
    
  275.       await act(() =>
    
  276.         dispatch(1, () => {
    
  277.           throw new Error('Expected to ignore the callback.');
    
  278.         }),
    
  279.       );
    
  280.     }).toErrorDev(
    
  281.       'State updates from the useState() and useReducer() Hooks ' +
    
  282.         "don't support the second callback argument. " +
    
  283.         'To execute a side effect after rendering, ' +
    
  284.         'declare it in the component body with useEffect().',
    
  285.       {withoutStack: true},
    
  286.     );
    
  287.     assertLog(['Count: 1']);
    
  288.     expect(root).toMatchRenderedOutput('1');
    
  289.   });
    
  290. 
    
  291.   it('never bails out if context has changed', async () => {
    
  292.     const {useState, useLayoutEffect, useContext} = React;
    
  293. 
    
  294.     const ThemeContext = React.createContext('light');
    
  295. 
    
  296.     let setTheme;
    
  297.     function ThemeProvider({children}) {
    
  298.       const [theme, _setTheme] = useState('light');
    
  299.       Scheduler.log('Theme: ' + theme);
    
  300.       setTheme = _setTheme;
    
  301.       return (
    
  302.         <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>
    
  303.       );
    
  304.     }
    
  305. 
    
  306.     function Child({text}) {
    
  307.       Scheduler.log('Child: ' + text);
    
  308.       return text;
    
  309.     }
    
  310. 
    
  311.     let setCounter;
    
  312.     function Parent() {
    
  313.       const [counter, _setCounter] = useState(0);
    
  314.       setCounter = _setCounter;
    
  315. 
    
  316.       const theme = useContext(ThemeContext);
    
  317. 
    
  318.       const text = `${counter} (${theme})`;
    
  319.       Scheduler.log(`Parent: ${text}`);
    
  320.       useLayoutEffect(() => {
    
  321.         Scheduler.log(`Effect: ${text}`);
    
  322.       });
    
  323.       return <Child text={text} />;
    
  324.     }
    
  325.     const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
    
  326.     await act(() => {
    
  327.       root.update(
    
  328.         <ThemeProvider>
    
  329.           <Parent />
    
  330.         </ThemeProvider>,
    
  331.       );
    
  332.     });
    
  333. 
    
  334.     assertLog([
    
  335.       'Theme: light',
    
  336.       'Parent: 0 (light)',
    
  337.       'Child: 0 (light)',
    
  338.       'Effect: 0 (light)',
    
  339.     ]);
    
  340.     expect(root).toMatchRenderedOutput('0 (light)');
    
  341. 
    
  342.     // Updating the theme to the same value doesn't cause the consumers
    
  343.     // to re-render.
    
  344.     setTheme('light');
    
  345.     await waitForAll([]);
    
  346.     expect(root).toMatchRenderedOutput('0 (light)');
    
  347. 
    
  348.     // Normal update
    
  349.     await act(() => setCounter(1));
    
  350.     assertLog(['Parent: 1 (light)', 'Child: 1 (light)', 'Effect: 1 (light)']);
    
  351.     expect(root).toMatchRenderedOutput('1 (light)');
    
  352. 
    
  353.     // Update that doesn't change state, so it bails out
    
  354.     await act(() => setCounter(1));
    
  355.     assertLog(['Parent: 1 (light)']);
    
  356.     expect(root).toMatchRenderedOutput('1 (light)');
    
  357. 
    
  358.     // Update that doesn't change state, but the context changes, too, so it
    
  359.     // can't bail out
    
  360.     await act(() => {
    
  361.       setCounter(1);
    
  362.       setTheme('dark');
    
  363.     });
    
  364. 
    
  365.     assertLog([
    
  366.       'Theme: dark',
    
  367.       'Parent: 1 (dark)',
    
  368.       'Child: 1 (dark)',
    
  369.       'Effect: 1 (dark)',
    
  370.     ]);
    
  371.     expect(root).toMatchRenderedOutput('1 (dark)');
    
  372.   });
    
  373. 
    
  374.   it('can bail out without calling render phase (as an optimization) if queue is known to be empty', async () => {
    
  375.     const {useState, useLayoutEffect} = React;
    
  376. 
    
  377.     function Child({text}) {
    
  378.       Scheduler.log('Child: ' + text);
    
  379.       return text;
    
  380.     }
    
  381. 
    
  382.     let setCounter;
    
  383.     function Parent() {
    
  384.       const [counter, _setCounter] = useState(0);
    
  385.       setCounter = _setCounter;
    
  386.       Scheduler.log('Parent: ' + counter);
    
  387.       useLayoutEffect(() => {
    
  388.         Scheduler.log('Effect: ' + counter);
    
  389.       });
    
  390.       return <Child text={counter} />;
    
  391.     }
    
  392. 
    
  393.     const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
    
  394.     root.update(<Parent />);
    
  395.     await waitForAll(['Parent: 0', 'Child: 0', 'Effect: 0']);
    
  396.     expect(root).toMatchRenderedOutput('0');
    
  397. 
    
  398.     // Normal update
    
  399.     await act(() => setCounter(1));
    
  400.     assertLog(['Parent: 1', 'Child: 1', 'Effect: 1']);
    
  401.     expect(root).toMatchRenderedOutput('1');
    
  402. 
    
  403.     // Update to the same state. React doesn't know if the queue is empty
    
  404.     // because the alternate fiber has pending update priority, so we have to
    
  405.     // enter the render phase before we can bail out. But we bail out before
    
  406.     // rendering the child, and we don't fire any effects.
    
  407.     await act(() => setCounter(1));
    
  408.     assertLog(['Parent: 1']);
    
  409.     expect(root).toMatchRenderedOutput('1');
    
  410. 
    
  411.     // Update to the same state again. This times, neither fiber has pending
    
  412.     // update priority, so we can bail out before even entering the render phase.
    
  413.     await act(() => setCounter(1));
    
  414.     await waitForAll([]);
    
  415.     expect(root).toMatchRenderedOutput('1');
    
  416. 
    
  417.     // This changes the state to something different so it renders normally.
    
  418.     await act(() => setCounter(2));
    
  419.     assertLog(['Parent: 2', 'Child: 2', 'Effect: 2']);
    
  420.     expect(root).toMatchRenderedOutput('2');
    
  421. 
    
  422.     // prepare to check SameValue
    
  423.     await act(() => {
    
  424.       setCounter(0);
    
  425.     });
    
  426.     assertLog(['Parent: 0', 'Child: 0', 'Effect: 0']);
    
  427.     expect(root).toMatchRenderedOutput('0');
    
  428. 
    
  429.     // Update to the same state for the first time to flush the queue
    
  430.     await act(() => {
    
  431.       setCounter(0);
    
  432.     });
    
  433. 
    
  434.     assertLog(['Parent: 0']);
    
  435.     expect(root).toMatchRenderedOutput('0');
    
  436. 
    
  437.     // Update again to the same state. Should bail out.
    
  438.     await act(() => {
    
  439.       setCounter(0);
    
  440.     });
    
  441.     await waitForAll([]);
    
  442.     expect(root).toMatchRenderedOutput('0');
    
  443. 
    
  444.     // Update to a different state (positive 0 to negative 0)
    
  445.     await act(() => {
    
  446.       setCounter(0 / -1);
    
  447.     });
    
  448.     assertLog(['Parent: 0', 'Child: 0', 'Effect: 0']);
    
  449.     expect(root).toMatchRenderedOutput('0');
    
  450.   });
    
  451. 
    
  452.   it('bails out multiple times in a row without entering render phase', async () => {
    
  453.     const {useState} = React;
    
  454. 
    
  455.     function Child({text}) {
    
  456.       Scheduler.log('Child: ' + text);
    
  457.       return text;
    
  458.     }
    
  459. 
    
  460.     let setCounter;
    
  461.     function Parent() {
    
  462.       const [counter, _setCounter] = useState(0);
    
  463.       setCounter = _setCounter;
    
  464.       Scheduler.log('Parent: ' + counter);
    
  465.       return <Child text={counter} />;
    
  466.     }
    
  467. 
    
  468.     const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
    
  469.     root.update(<Parent />);
    
  470.     await waitForAll(['Parent: 0', 'Child: 0']);
    
  471.     expect(root).toMatchRenderedOutput('0');
    
  472. 
    
  473.     const update = value => {
    
  474.       setCounter(previous => {
    
  475.         Scheduler.log(`Compute state (${previous} -> ${value})`);
    
  476.         return value;
    
  477.       });
    
  478.     };
    
  479.     ReactTestRenderer.unstable_batchedUpdates(() => {
    
  480.       update(0);
    
  481.       update(0);
    
  482.       update(0);
    
  483.       update(1);
    
  484.       update(2);
    
  485.       update(3);
    
  486.     });
    
  487. 
    
  488.     assertLog([
    
  489.       // The first four updates were eagerly computed, because the queue is
    
  490.       // empty before each one.
    
  491.       'Compute state (0 -> 0)',
    
  492.       'Compute state (0 -> 0)',
    
  493.       'Compute state (0 -> 0)',
    
  494.       // The fourth update doesn't bail out
    
  495.       'Compute state (0 -> 1)',
    
  496.       // so subsequent updates can't be eagerly computed.
    
  497.     ]);
    
  498. 
    
  499.     // Now let's enter the render phase
    
  500.     await waitForAll([
    
  501.       // We don't need to re-compute the first four updates. Only the final two.
    
  502.       'Compute state (1 -> 2)',
    
  503.       'Compute state (2 -> 3)',
    
  504.       'Parent: 3',
    
  505.       'Child: 3',
    
  506.     ]);
    
  507.     expect(root).toMatchRenderedOutput('3');
    
  508.   });
    
  509. 
    
  510.   it('can rebase on top of a previously skipped update', async () => {
    
  511.     const {useState} = React;
    
  512. 
    
  513.     function Child({text}) {
    
  514.       Scheduler.log('Child: ' + text);
    
  515.       return text;
    
  516.     }
    
  517. 
    
  518.     let setCounter;
    
  519.     function Parent() {
    
  520.       const [counter, _setCounter] = useState(1);
    
  521.       setCounter = _setCounter;
    
  522.       Scheduler.log('Parent: ' + counter);
    
  523.       return <Child text={counter} />;
    
  524.     }
    
  525. 
    
  526.     const root = ReactTestRenderer.create(null, {unstable_isConcurrent: true});
    
  527.     root.update(<Parent />);
    
  528.     await waitForAll(['Parent: 1', 'Child: 1']);
    
  529.     expect(root).toMatchRenderedOutput('1');
    
  530. 
    
  531.     const update = compute => {
    
  532.       setCounter(previous => {
    
  533.         const value = compute(previous);
    
  534.         Scheduler.log(`Compute state (${previous} -> ${value})`);
    
  535.         return value;
    
  536.       });
    
  537.     };
    
  538. 
    
  539.     if (gate(flags => flags.enableUnifiedSyncLane)) {
    
  540.       // Update at transition priority
    
  541.       React.startTransition(() => update(n => n * 100));
    
  542.     } else {
    
  543.       // Update at normal priority
    
  544.       ReactTestRenderer.unstable_batchedUpdates(() => update(n => n * 100));
    
  545.     }
    
  546.     // The new state is eagerly computed.
    
  547.     assertLog(['Compute state (1 -> 100)']);
    
  548. 
    
  549.     // but before it's flushed, a higher priority update interrupts it.
    
  550.     root.unstable_flushSync(() => {
    
  551.       update(n => n + 5);
    
  552.     });
    
  553.     assertLog([
    
  554.       // The eagerly computed state was completely skipped
    
  555.       'Compute state (1 -> 6)',
    
  556.       'Parent: 6',
    
  557.       'Child: 6',
    
  558.     ]);
    
  559.     expect(root).toMatchRenderedOutput('6');
    
  560. 
    
  561.     // Now when we finish the first update, the second update is rebased on top.
    
  562.     // Notice we didn't have to recompute the first update even though it was
    
  563.     // skipped in the previous render.
    
  564.     await waitForAll([
    
  565.       'Compute state (100 -> 105)',
    
  566.       'Parent: 105',
    
  567.       'Child: 105',
    
  568.     ]);
    
  569.     expect(root).toMatchRenderedOutput('105');
    
  570.   });
    
  571. 
    
  572.   it('warns about variable number of dependencies', () => {
    
  573.     const {useLayoutEffect} = React;
    
  574.     function App(props) {
    
  575.       useLayoutEffect(() => {
    
  576.         Scheduler.log('Did commit: ' + props.dependencies.join(', '));
    
  577.       }, props.dependencies);
    
  578.       return props.dependencies;
    
  579.     }
    
  580.     const root = ReactTestRenderer.create(<App dependencies={['A']} />);
    
  581.     assertLog(['Did commit: A']);
    
  582.     expect(() => {
    
  583.       root.update(<App dependencies={['A', 'B']} />);
    
  584.     }).toErrorDev([
    
  585.       'Warning: The final argument passed to useLayoutEffect changed size ' +
    
  586.         'between renders. The order and size of this array must remain ' +
    
  587.         'constant.\n\n' +
    
  588.         'Previous: [A]\n' +
    
  589.         'Incoming: [A, B]\n',
    
  590.     ]);
    
  591.   });
    
  592. 
    
  593.   it('warns if switching from dependencies to no dependencies', () => {
    
  594.     const {useMemo} = React;
    
  595.     function App({text, hasDeps}) {
    
  596.       const resolvedText = useMemo(
    
  597.         () => {
    
  598.           Scheduler.log('Compute');
    
  599.           return text.toUpperCase();
    
  600.         },
    
  601.         hasDeps ? null : [text],
    
  602.       );
    
  603.       return resolvedText;
    
  604.     }
    
  605. 
    
  606.     const root = ReactTestRenderer.create(null);
    
  607.     root.update(<App text="Hello" hasDeps={true} />);
    
  608.     assertLog(['Compute']);
    
  609.     expect(root).toMatchRenderedOutput('HELLO');
    
  610. 
    
  611.     expect(() => {
    
  612.       root.update(<App text="Hello" hasDeps={false} />);
    
  613.     }).toErrorDev([
    
  614.       'Warning: useMemo received a final argument during this render, but ' +
    
  615.         'not during the previous render. Even though the final argument is ' +
    
  616.         'optional, its type cannot change between renders.',
    
  617.     ]);
    
  618.   });
    
  619. 
    
  620.   it('warns if deps is not an array', async () => {
    
  621.     const {useEffect, useLayoutEffect, useMemo, useCallback} = React;
    
  622. 
    
  623.     function App(props) {
    
  624.       useEffect(() => {}, props.deps);
    
  625.       useLayoutEffect(() => {}, props.deps);
    
  626.       useMemo(() => {}, props.deps);
    
  627.       useCallback(() => {}, props.deps);
    
  628.       return null;
    
  629.     }
    
  630. 
    
  631.     await expect(async () => {
    
  632.       await act(() => {
    
  633.         ReactTestRenderer.create(<App deps={'hello'} />);
    
  634.       });
    
  635.     }).toErrorDev([
    
  636.       'Warning: useEffect received a final argument that is not an array (instead, received `string`). ' +
    
  637.         'When specified, the final argument must be an array.',
    
  638.       'Warning: useLayoutEffect received a final argument that is not an array (instead, received `string`). ' +
    
  639.         'When specified, the final argument must be an array.',
    
  640.       'Warning: useMemo received a final argument that is not an array (instead, received `string`). ' +
    
  641.         'When specified, the final argument must be an array.',
    
  642.       'Warning: useCallback received a final argument that is not an array (instead, received `string`). ' +
    
  643.         'When specified, the final argument must be an array.',
    
  644.     ]);
    
  645.     await expect(async () => {
    
  646.       await act(() => {
    
  647.         ReactTestRenderer.create(<App deps={100500} />);
    
  648.       });
    
  649.     }).toErrorDev([
    
  650.       'Warning: useEffect received a final argument that is not an array (instead, received `number`). ' +
    
  651.         'When specified, the final argument must be an array.',
    
  652.       'Warning: useLayoutEffect received a final argument that is not an array (instead, received `number`). ' +
    
  653.         'When specified, the final argument must be an array.',
    
  654.       'Warning: useMemo received a final argument that is not an array (instead, received `number`). ' +
    
  655.         'When specified, the final argument must be an array.',
    
  656.       'Warning: useCallback received a final argument that is not an array (instead, received `number`). ' +
    
  657.         'When specified, the final argument must be an array.',
    
  658.     ]);
    
  659.     await expect(async () => {
    
  660.       await act(() => {
    
  661.         ReactTestRenderer.create(<App deps={{}} />);
    
  662.       });
    
  663.     }).toErrorDev([
    
  664.       'Warning: useEffect received a final argument that is not an array (instead, received `object`). ' +
    
  665.         'When specified, the final argument must be an array.',
    
  666.       'Warning: useLayoutEffect received a final argument that is not an array (instead, received `object`). ' +
    
  667.         'When specified, the final argument must be an array.',
    
  668.       'Warning: useMemo received a final argument that is not an array (instead, received `object`). ' +
    
  669.         'When specified, the final argument must be an array.',
    
  670.       'Warning: useCallback received a final argument that is not an array (instead, received `object`). ' +
    
  671.         'When specified, the final argument must be an array.',
    
  672.     ]);
    
  673. 
    
  674.     await act(() => {
    
  675.       ReactTestRenderer.create(<App deps={[]} />);
    
  676.       ReactTestRenderer.create(<App deps={null} />);
    
  677.       ReactTestRenderer.create(<App deps={undefined} />);
    
  678.     });
    
  679.   });
    
  680. 
    
  681.   it('warns if deps is not an array for useImperativeHandle', () => {
    
  682.     const {useImperativeHandle} = React;
    
  683. 
    
  684.     const App = React.forwardRef((props, ref) => {
    
  685.       useImperativeHandle(ref, () => {}, props.deps);
    
  686.       return null;
    
  687.     });
    
  688. 
    
  689.     expect(() => {
    
  690.       ReactTestRenderer.create(<App deps={'hello'} />);
    
  691.     }).toErrorDev([
    
  692.       'Warning: useImperativeHandle received a final argument that is not an array (instead, received `string`). ' +
    
  693.         'When specified, the final argument must be an array.',
    
  694.     ]);
    
  695.     ReactTestRenderer.create(<App deps={[]} />);
    
  696.     ReactTestRenderer.create(<App deps={null} />);
    
  697.     ReactTestRenderer.create(<App deps={undefined} />);
    
  698.   });
    
  699. 
    
  700.   it('does not forget render phase useState updates inside an effect', async () => {
    
  701.     const {useState, useEffect} = React;
    
  702. 
    
  703.     function Counter() {
    
  704.       const [counter, setCounter] = useState(0);
    
  705.       if (counter === 0) {
    
  706.         setCounter(x => x + 1);
    
  707.         setCounter(x => x + 1);
    
  708.       }
    
  709.       useEffect(() => {
    
  710.         setCounter(x => x + 1);
    
  711.         setCounter(x => x + 1);
    
  712.       }, []);
    
  713.       return counter;
    
  714.     }
    
  715. 
    
  716.     const root = ReactTestRenderer.create(null);
    
  717.     await act(() => {
    
  718.       root.update(<Counter />);
    
  719.     });
    
  720.     expect(root).toMatchRenderedOutput('4');
    
  721.   });
    
  722. 
    
  723.   it('does not forget render phase useReducer updates inside an effect with hoisted reducer', async () => {
    
  724.     const {useReducer, useEffect} = React;
    
  725. 
    
  726.     const reducer = x => x + 1;
    
  727.     function Counter() {
    
  728.       const [counter, increment] = useReducer(reducer, 0);
    
  729.       if (counter === 0) {
    
  730.         increment();
    
  731.         increment();
    
  732.       }
    
  733.       useEffect(() => {
    
  734.         increment();
    
  735.         increment();
    
  736.       }, []);
    
  737.       return counter;
    
  738.     }
    
  739. 
    
  740.     const root = ReactTestRenderer.create(null);
    
  741.     await act(() => {
    
  742.       root.update(<Counter />);
    
  743.     });
    
  744.     expect(root).toMatchRenderedOutput('4');
    
  745.   });
    
  746. 
    
  747.   it('does not forget render phase useReducer updates inside an effect with inline reducer', async () => {
    
  748.     const {useReducer, useEffect} = React;
    
  749. 
    
  750.     function Counter() {
    
  751.       const [counter, increment] = useReducer(x => x + 1, 0);
    
  752.       if (counter === 0) {
    
  753.         increment();
    
  754.         increment();
    
  755.       }
    
  756.       useEffect(() => {
    
  757.         increment();
    
  758.         increment();
    
  759.       }, []);
    
  760.       return counter;
    
  761.     }
    
  762. 
    
  763.     const root = ReactTestRenderer.create(null);
    
  764.     await act(() => {
    
  765.       root.update(<Counter />);
    
  766.     });
    
  767.     expect(root).toMatchRenderedOutput('4');
    
  768.   });
    
  769. 
    
  770.   it('warns for bad useImperativeHandle first arg', () => {
    
  771.     const {useImperativeHandle} = React;
    
  772.     function App() {
    
  773.       useImperativeHandle({
    
  774.         focus() {},
    
  775.       });
    
  776.       return null;
    
  777.     }
    
  778. 
    
  779.     expect(() => {
    
  780.       expect(() => {
    
  781.         ReactTestRenderer.create(<App />);
    
  782.       }).toThrow('create is not a function');
    
  783.     }).toErrorDev([
    
  784.       'Expected useImperativeHandle() first argument to either be a ' +
    
  785.         'ref callback or React.createRef() object. ' +
    
  786.         'Instead received: an object with keys {focus}.',
    
  787.       'Expected useImperativeHandle() second argument to be a function ' +
    
  788.         'that creates a handle. Instead received: undefined.',
    
  789.     ]);
    
  790.   });
    
  791. 
    
  792.   it('warns for bad useImperativeHandle second arg', () => {
    
  793.     const {useImperativeHandle} = React;
    
  794.     const App = React.forwardRef((props, ref) => {
    
  795.       useImperativeHandle(ref, {
    
  796.         focus() {},
    
  797.       });
    
  798.       return null;
    
  799.     });
    
  800. 
    
  801.     expect(() => {
    
  802.       ReactTestRenderer.create(<App />);
    
  803.     }).toErrorDev([
    
  804.       'Expected useImperativeHandle() second argument to be a function ' +
    
  805.         'that creates a handle. Instead received: object.',
    
  806.     ]);
    
  807.   });
    
  808. 
    
  809.   // https://github.com/facebook/react/issues/14022
    
  810.   it('works with ReactDOMServer calls inside a component', () => {
    
  811.     const {useState} = React;
    
  812.     function App(props) {
    
  813.       const markup1 = ReactDOMServer.renderToString(<p>hello</p>);
    
  814.       const markup2 = ReactDOMServer.renderToStaticMarkup(<p>bye</p>);
    
  815.       const [counter] = useState(0);
    
  816.       return markup1 + counter + markup2;
    
  817.     }
    
  818.     const root = ReactTestRenderer.create(<App />);
    
  819.     expect(root.toJSON()).toMatchSnapshot();
    
  820.   });
    
  821. 
    
  822.   it("throws when calling hooks inside .memo's compare function", () => {
    
  823.     const {useState} = React;
    
  824.     function App() {
    
  825.       useState(0);
    
  826.       return null;
    
  827.     }
    
  828.     const MemoApp = React.memo(App, () => {
    
  829.       useState(0);
    
  830.       return false;
    
  831.     });
    
  832. 
    
  833.     const root = ReactTestRenderer.create(<MemoApp />);
    
  834.     // trying to render again should trigger comparison and throw
    
  835.     expect(() => root.update(<MemoApp />)).toThrow(
    
  836.       'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
    
  837.         ' one of the following reasons:\n' +
    
  838.         '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
    
  839.         '2. You might be breaking the Rules of Hooks\n' +
    
  840.         '3. You might have more than one copy of React in the same app\n' +
    
  841.         'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.',
    
  842.     );
    
  843.     // the next round, it does a fresh mount, so should render
    
  844.     expect(() => root.update(<MemoApp />)).not.toThrow(
    
  845.       'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
    
  846.         ' one of the following reasons:\n' +
    
  847.         '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
    
  848.         '2. You might be breaking the Rules of Hooks\n' +
    
  849.         '3. You might have more than one copy of React in the same app\n' +
    
  850.         'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.',
    
  851.     );
    
  852.     // and then again, fail
    
  853.     expect(() => root.update(<MemoApp />)).toThrow(
    
  854.       'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
    
  855.         ' one of the following reasons:\n' +
    
  856.         '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
    
  857.         '2. You might be breaking the Rules of Hooks\n' +
    
  858.         '3. You might have more than one copy of React in the same app\n' +
    
  859.         'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.',
    
  860.     );
    
  861.   });
    
  862. 
    
  863.   it('warns when calling hooks inside useMemo', () => {
    
  864.     const {useMemo, useState} = React;
    
  865.     function App() {
    
  866.       useMemo(() => {
    
  867.         useState(0);
    
  868.       });
    
  869.       return null;
    
  870.     }
    
  871.     expect(() => ReactTestRenderer.create(<App />)).toErrorDev(
    
  872.       'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks.',
    
  873.     );
    
  874.   });
    
  875. 
    
  876.   it('warns when reading context inside useMemo', () => {
    
  877.     const {useMemo, createContext} = React;
    
  878.     const ReactCurrentDispatcher =
    
  879.       React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
    
  880.         .ReactCurrentDispatcher;
    
  881. 
    
  882.     const ThemeContext = createContext('light');
    
  883.     function App() {
    
  884.       return useMemo(() => {
    
  885.         return ReactCurrentDispatcher.current.readContext(ThemeContext);
    
  886.       }, []);
    
  887.     }
    
  888. 
    
  889.     expect(() => ReactTestRenderer.create(<App />)).toErrorDev(
    
  890.       'Context can only be read while React is rendering',
    
  891.     );
    
  892.   });
    
  893. 
    
  894.   it('warns when reading context inside useMemo after reading outside it', () => {
    
  895.     const {useMemo, createContext} = React;
    
  896.     const ReactCurrentDispatcher =
    
  897.       React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
    
  898.         .ReactCurrentDispatcher;
    
  899. 
    
  900.     const ThemeContext = createContext('light');
    
  901.     let firstRead, secondRead;
    
  902.     function App() {
    
  903.       firstRead = ReactCurrentDispatcher.current.readContext(ThemeContext);
    
  904.       useMemo(() => {});
    
  905.       secondRead = ReactCurrentDispatcher.current.readContext(ThemeContext);
    
  906.       return useMemo(() => {
    
  907.         return ReactCurrentDispatcher.current.readContext(ThemeContext);
    
  908.       }, []);
    
  909.     }
    
  910. 
    
  911.     expect(() => ReactTestRenderer.create(<App />)).toErrorDev(
    
  912.       'Context can only be read while React is rendering',
    
  913.     );
    
  914.     expect(firstRead).toBe('light');
    
  915.     expect(secondRead).toBe('light');
    
  916.   });
    
  917. 
    
  918.   // Throws because there's no runtime cost for being strict here.
    
  919.   it('throws when reading context inside useEffect', async () => {
    
  920.     const {useEffect, createContext} = React;
    
  921.     const ReactCurrentDispatcher =
    
  922.       React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
    
  923.         .ReactCurrentDispatcher;
    
  924. 
    
  925.     const ThemeContext = createContext('light');
    
  926.     function App() {
    
  927.       useEffect(() => {
    
  928.         ReactCurrentDispatcher.current.readContext(ThemeContext);
    
  929.       });
    
  930.       return null;
    
  931.     }
    
  932. 
    
  933.     await act(async () => {
    
  934.       ReactTestRenderer.create(<App />);
    
  935.       // The exact message doesn't matter, just make sure we don't allow this
    
  936.       await waitForThrow('Context can only be read while React is rendering');
    
  937.     });
    
  938.   });
    
  939. 
    
  940.   // Throws because there's no runtime cost for being strict here.
    
  941.   it('throws when reading context inside useLayoutEffect', () => {
    
  942.     const {useLayoutEffect, createContext} = React;
    
  943.     const ReactCurrentDispatcher =
    
  944.       React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
    
  945.         .ReactCurrentDispatcher;
    
  946. 
    
  947.     const ThemeContext = createContext('light');
    
  948.     function App() {
    
  949.       useLayoutEffect(() => {
    
  950.         ReactCurrentDispatcher.current.readContext(ThemeContext);
    
  951.       });
    
  952.       return null;
    
  953.     }
    
  954. 
    
  955.     expect(() => ReactTestRenderer.create(<App />)).toThrow(
    
  956.       // The exact message doesn't matter, just make sure we don't allow this
    
  957.       'Context can only be read while React is rendering',
    
  958.     );
    
  959.   });
    
  960. 
    
  961.   it('warns when reading context inside useReducer', () => {
    
  962.     const {useReducer, createContext} = React;
    
  963.     const ReactCurrentDispatcher =
    
  964.       React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
    
  965.         .ReactCurrentDispatcher;
    
  966. 
    
  967.     const ThemeContext = createContext('light');
    
  968.     function App() {
    
  969.       const [state, dispatch] = useReducer((s, action) => {
    
  970.         ReactCurrentDispatcher.current.readContext(ThemeContext);
    
  971.         return action;
    
  972.       }, 0);
    
  973.       if (state === 0) {
    
  974.         dispatch(1);
    
  975.       }
    
  976.       return null;
    
  977.     }
    
  978. 
    
  979.     expect(() => ReactTestRenderer.create(<App />)).toErrorDev([
    
  980.       'Context can only be read while React is rendering',
    
  981.     ]);
    
  982.   });
    
  983. 
    
  984.   // Edge case.
    
  985.   it('warns when reading context inside eager useReducer', () => {
    
  986.     const {useState, createContext} = React;
    
  987.     const ThemeContext = createContext('light');
    
  988. 
    
  989.     const ReactCurrentDispatcher =
    
  990.       React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
    
  991.         .ReactCurrentDispatcher;
    
  992. 
    
  993.     let _setState;
    
  994.     function Fn() {
    
  995.       const [, setState] = useState(0);
    
  996.       _setState = setState;
    
  997.       return null;
    
  998.     }
    
  999. 
    
  1000.     class Cls extends React.Component {
    
  1001.       render() {
    
  1002.         _setState(() =>
    
  1003.           ReactCurrentDispatcher.current.readContext(ThemeContext),
    
  1004.         );
    
  1005. 
    
  1006.         return null;
    
  1007.       }
    
  1008.     }
    
  1009. 
    
  1010.     expect(() =>
    
  1011.       ReactTestRenderer.create(
    
  1012.         <>
    
  1013.           <Fn />
    
  1014.           <Cls />
    
  1015.         </>,
    
  1016.       ),
    
  1017.     ).toErrorDev([
    
  1018.       'Context can only be read while React is rendering',
    
  1019.       'Cannot update a component (`Fn`) while rendering a different component (`Cls`).',
    
  1020.     ]);
    
  1021.   });
    
  1022. 
    
  1023.   it('warns when calling hooks inside useReducer', () => {
    
  1024.     const {useReducer, useState, useRef} = React;
    
  1025. 
    
  1026.     function App() {
    
  1027.       const [value, dispatch] = useReducer((state, action) => {
    
  1028.         useRef(0);
    
  1029.         return state + 1;
    
  1030.       }, 0);
    
  1031.       if (value === 0) {
    
  1032.         dispatch('foo');
    
  1033.       }
    
  1034.       useState();
    
  1035.       return value;
    
  1036.     }
    
  1037. 
    
  1038.     expect(() => {
    
  1039.       expect(() => {
    
  1040.         ReactTestRenderer.create(<App />);
    
  1041.       }).toThrow(
    
  1042.         'Update hook called on initial render. This is likely a bug in React. Please file an issue.',
    
  1043.       );
    
  1044.     }).toErrorDev([
    
  1045.       'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks',
    
  1046.       'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks',
    
  1047.       'Warning: React has detected a change in the order of Hooks called by App. ' +
    
  1048.         'This will lead to bugs and errors if not fixed. For more information, ' +
    
  1049.         'read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks\n\n' +
    
  1050.         '   Previous render            Next render\n' +
    
  1051.         '   ------------------------------------------------------\n' +
    
  1052.         '1. useReducer                 useReducer\n' +
    
  1053.         '2. useState                   useRef\n' +
    
  1054.         '   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n',
    
  1055.     ]);
    
  1056.   });
    
  1057. 
    
  1058.   it("warns when calling hooks inside useState's initialize function", () => {
    
  1059.     const {useState, useRef} = React;
    
  1060.     function App() {
    
  1061.       useState(() => {
    
  1062.         useRef(0);
    
  1063.         return 0;
    
  1064.       });
    
  1065.       return null;
    
  1066.     }
    
  1067.     expect(() => ReactTestRenderer.create(<App />)).toErrorDev(
    
  1068.       'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks.',
    
  1069.     );
    
  1070.   });
    
  1071. 
    
  1072.   it('resets warning internal state when interrupted by an error', async () => {
    
  1073.     const ReactCurrentDispatcher =
    
  1074.       React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
    
  1075.         .ReactCurrentDispatcher;
    
  1076. 
    
  1077.     const ThemeContext = React.createContext('light');
    
  1078.     function App() {
    
  1079.       React.useMemo(() => {
    
  1080.         // Trigger warnings
    
  1081.         ReactCurrentDispatcher.current.readContext(ThemeContext);
    
  1082.         React.useRef();
    
  1083.         // Interrupt exit from a Hook
    
  1084.         throw new Error('No.');
    
  1085.       }, []);
    
  1086.     }
    
  1087. 
    
  1088.     class Boundary extends React.Component {
    
  1089.       state = {};
    
  1090.       static getDerivedStateFromError(error) {
    
  1091.         return {err: true};
    
  1092.       }
    
  1093.       render() {
    
  1094.         if (this.state.err) {
    
  1095.           return 'Oops';
    
  1096.         }
    
  1097.         return this.props.children;
    
  1098.       }
    
  1099.     }
    
  1100. 
    
  1101.     expect(() => {
    
  1102.       ReactTestRenderer.create(
    
  1103.         <Boundary>
    
  1104.           <App />
    
  1105.         </Boundary>,
    
  1106.       );
    
  1107.     }).toErrorDev([
    
  1108.       // We see it twice due to replay
    
  1109.       'Context can only be read while React is rendering',
    
  1110.       'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks',
    
  1111.       'Context can only be read while React is rendering',
    
  1112.       'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks',
    
  1113.     ]);
    
  1114. 
    
  1115.     function Valid() {
    
  1116.       React.useState();
    
  1117.       React.useMemo(() => {});
    
  1118.       React.useReducer(() => {});
    
  1119.       React.useEffect(() => {});
    
  1120.       React.useLayoutEffect(() => {});
    
  1121.       React.useCallback(() => {});
    
  1122.       React.useRef();
    
  1123.       React.useImperativeHandle(
    
  1124.         () => {},
    
  1125.         () => {},
    
  1126.       );
    
  1127.       if (__DEV__) {
    
  1128.         React.useDebugValue();
    
  1129.       }
    
  1130.       return null;
    
  1131.     }
    
  1132.     // Verify it doesn't think we're still inside a Hook.
    
  1133.     // Should have no warnings.
    
  1134.     await act(() => {
    
  1135.       ReactTestRenderer.create(<Valid />);
    
  1136.     });
    
  1137. 
    
  1138.     // Verify warnings don't get permanently disabled.
    
  1139.     expect(() => {
    
  1140.       ReactTestRenderer.create(
    
  1141.         <Boundary>
    
  1142.           <App />
    
  1143.         </Boundary>,
    
  1144.       );
    
  1145.     }).toErrorDev([
    
  1146.       // We see it twice due to replay
    
  1147.       'Context can only be read while React is rendering',
    
  1148.       'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks',
    
  1149.       'Context can only be read while React is rendering',
    
  1150.       'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks',
    
  1151.     ]);
    
  1152.   });
    
  1153. 
    
  1154.   it('warns when reading context inside useMemo', () => {
    
  1155.     const {useMemo, createContext} = React;
    
  1156.     const ReactCurrentDispatcher =
    
  1157.       React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
    
  1158.         .ReactCurrentDispatcher;
    
  1159. 
    
  1160.     const ThemeContext = createContext('light');
    
  1161.     function App() {
    
  1162.       return useMemo(() => {
    
  1163.         return ReactCurrentDispatcher.current.readContext(ThemeContext);
    
  1164.       }, []);
    
  1165.     }
    
  1166. 
    
  1167.     expect(() => ReactTestRenderer.create(<App />)).toErrorDev(
    
  1168.       'Context can only be read while React is rendering',
    
  1169.     );
    
  1170.   });
    
  1171. 
    
  1172.   it('double-invokes components with Hooks in Strict Mode', () => {
    
  1173.     ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = true;
    
  1174. 
    
  1175.     const {useState, StrictMode} = React;
    
  1176.     let renderCount = 0;
    
  1177. 
    
  1178.     function NoHooks() {
    
  1179.       renderCount++;
    
  1180.       return <div />;
    
  1181.     }
    
  1182. 
    
  1183.     function HasHooks() {
    
  1184.       useState(0);
    
  1185.       renderCount++;
    
  1186.       return <div />;
    
  1187.     }
    
  1188. 
    
  1189.     const FwdRef = React.forwardRef((props, ref) => {
    
  1190.       renderCount++;
    
  1191.       return <div />;
    
  1192.     });
    
  1193. 
    
  1194.     const FwdRefHasHooks = React.forwardRef((props, ref) => {
    
  1195.       useState(0);
    
  1196.       renderCount++;
    
  1197.       return <div />;
    
  1198.     });
    
  1199. 
    
  1200.     const Memo = React.memo(props => {
    
  1201.       renderCount++;
    
  1202.       return <div />;
    
  1203.     });
    
  1204. 
    
  1205.     const MemoHasHooks = React.memo(props => {
    
  1206.       useState(0);
    
  1207.       renderCount++;
    
  1208.       return <div />;
    
  1209.     });
    
  1210. 
    
  1211.     function Factory() {
    
  1212.       return {
    
  1213.         state: {},
    
  1214.         render() {
    
  1215.           renderCount++;
    
  1216.           return <div />;
    
  1217.         },
    
  1218.       };
    
  1219.     }
    
  1220. 
    
  1221.     const renderer = ReactTestRenderer.create(null);
    
  1222. 
    
  1223.     renderCount = 0;
    
  1224.     renderer.update(<NoHooks />);
    
  1225.     expect(renderCount).toBe(1);
    
  1226.     renderCount = 0;
    
  1227.     renderer.update(<NoHooks />);
    
  1228.     expect(renderCount).toBe(1);
    
  1229.     renderCount = 0;
    
  1230.     renderer.update(
    
  1231.       <StrictMode>
    
  1232.         <NoHooks />
    
  1233.       </StrictMode>,
    
  1234.     );
    
  1235.     expect(renderCount).toBe(__DEV__ ? 2 : 1);
    
  1236.     renderCount = 0;
    
  1237.     renderer.update(
    
  1238.       <StrictMode>
    
  1239.         <NoHooks />
    
  1240.       </StrictMode>,
    
  1241.     );
    
  1242.     expect(renderCount).toBe(__DEV__ ? 2 : 1);
    
  1243. 
    
  1244.     renderCount = 0;
    
  1245.     renderer.update(<FwdRef />);
    
  1246.     expect(renderCount).toBe(1);
    
  1247.     renderCount = 0;
    
  1248.     renderer.update(<FwdRef />);
    
  1249.     expect(renderCount).toBe(1);
    
  1250.     renderCount = 0;
    
  1251.     renderer.update(
    
  1252.       <StrictMode>
    
  1253.         <FwdRef />
    
  1254.       </StrictMode>,
    
  1255.     );
    
  1256.     expect(renderCount).toBe(__DEV__ ? 2 : 1);
    
  1257.     renderCount = 0;
    
  1258.     renderer.update(
    
  1259.       <StrictMode>
    
  1260.         <FwdRef />
    
  1261.       </StrictMode>,
    
  1262.     );
    
  1263.     expect(renderCount).toBe(__DEV__ ? 2 : 1);
    
  1264. 
    
  1265.     renderCount = 0;
    
  1266.     renderer.update(<Memo arg={1} />);
    
  1267.     expect(renderCount).toBe(1);
    
  1268.     renderCount = 0;
    
  1269.     renderer.update(<Memo arg={2} />);
    
  1270.     expect(renderCount).toBe(1);
    
  1271.     renderCount = 0;
    
  1272.     renderer.update(
    
  1273.       <StrictMode>
    
  1274.         <Memo arg={1} />
    
  1275.       </StrictMode>,
    
  1276.     );
    
  1277.     expect(renderCount).toBe(__DEV__ ? 2 : 1);
    
  1278.     renderCount = 0;
    
  1279.     renderer.update(
    
  1280.       <StrictMode>
    
  1281.         <Memo arg={2} />
    
  1282.       </StrictMode>,
    
  1283.     );
    
  1284.     expect(renderCount).toBe(__DEV__ ? 2 : 1);
    
  1285. 
    
  1286.     if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) {
    
  1287.       renderCount = 0;
    
  1288.       expect(() => renderer.update(<Factory />)).toErrorDev(
    
  1289.         'Warning: The <Factory /> component appears to be a function component that returns a class instance. ' +
    
  1290.           'Change Factory to a class that extends React.Component instead. ' +
    
  1291.           "If you can't use a class try assigning the prototype on the function as a workaround. " +
    
  1292.           '`Factory.prototype = React.Component.prototype`. ' +
    
  1293.           "Don't use an arrow function since it cannot be called with `new` by React.",
    
  1294.       );
    
  1295.       expect(renderCount).toBe(1);
    
  1296.       renderCount = 0;
    
  1297.       renderer.update(<Factory />);
    
  1298.       expect(renderCount).toBe(1);
    
  1299. 
    
  1300.       renderCount = 0;
    
  1301.       renderer.update(
    
  1302.         <StrictMode>
    
  1303.           <Factory />
    
  1304.         </StrictMode>,
    
  1305.       );
    
  1306.       expect(renderCount).toBe(__DEV__ ? 2 : 1); // Treated like a class
    
  1307.       renderCount = 0;
    
  1308.       renderer.update(
    
  1309.         <StrictMode>
    
  1310.           <Factory />
    
  1311.         </StrictMode>,
    
  1312.       );
    
  1313.       expect(renderCount).toBe(__DEV__ ? 2 : 1); // Treated like a class
    
  1314.     }
    
  1315. 
    
  1316.     renderCount = 0;
    
  1317.     renderer.update(<HasHooks />);
    
  1318.     expect(renderCount).toBe(1);
    
  1319.     renderCount = 0;
    
  1320.     renderer.update(<HasHooks />);
    
  1321.     expect(renderCount).toBe(1);
    
  1322.     renderCount = 0;
    
  1323.     renderer.update(
    
  1324.       <StrictMode>
    
  1325.         <HasHooks />
    
  1326.       </StrictMode>,
    
  1327.     );
    
  1328.     expect(renderCount).toBe(__DEV__ ? 2 : 1); // Has Hooks
    
  1329.     renderCount = 0;
    
  1330.     renderer.update(
    
  1331.       <StrictMode>
    
  1332.         <HasHooks />
    
  1333.       </StrictMode>,
    
  1334.     );
    
  1335.     expect(renderCount).toBe(__DEV__ ? 2 : 1); // Has Hooks
    
  1336. 
    
  1337.     renderCount = 0;
    
  1338.     renderer.update(<FwdRefHasHooks />);
    
  1339.     expect(renderCount).toBe(1);
    
  1340.     renderCount = 0;
    
  1341.     renderer.update(<FwdRefHasHooks />);
    
  1342.     expect(renderCount).toBe(1);
    
  1343.     renderCount = 0;
    
  1344.     renderer.update(
    
  1345.       <StrictMode>
    
  1346.         <FwdRefHasHooks />
    
  1347.       </StrictMode>,
    
  1348.     );
    
  1349.     expect(renderCount).toBe(__DEV__ ? 2 : 1); // Has Hooks
    
  1350.     renderCount = 0;
    
  1351.     renderer.update(
    
  1352.       <StrictMode>
    
  1353.         <FwdRefHasHooks />
    
  1354.       </StrictMode>,
    
  1355.     );
    
  1356.     expect(renderCount).toBe(__DEV__ ? 2 : 1); // Has Hooks
    
  1357. 
    
  1358.     renderCount = 0;
    
  1359.     renderer.update(<MemoHasHooks arg={1} />);
    
  1360.     expect(renderCount).toBe(1);
    
  1361.     renderCount = 0;
    
  1362.     renderer.update(<MemoHasHooks arg={2} />);
    
  1363.     expect(renderCount).toBe(1);
    
  1364.     renderCount = 0;
    
  1365.     renderer.update(
    
  1366.       <StrictMode>
    
  1367.         <MemoHasHooks arg={1} />
    
  1368.       </StrictMode>,
    
  1369.     );
    
  1370.     expect(renderCount).toBe(__DEV__ ? 2 : 1); // Has Hooks
    
  1371.     renderCount = 0;
    
  1372.     renderer.update(
    
  1373.       <StrictMode>
    
  1374.         <MemoHasHooks arg={2} />
    
  1375.       </StrictMode>,
    
  1376.     );
    
  1377.     expect(renderCount).toBe(__DEV__ ? 2 : 1); // Has Hooks
    
  1378.   });
    
  1379. 
    
  1380.   it('double-invokes useMemo in DEV StrictMode despite []', () => {
    
  1381.     ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = true;
    
  1382.     const {useMemo, StrictMode} = React;
    
  1383. 
    
  1384.     let useMemoCount = 0;
    
  1385.     function BadUseMemo() {
    
  1386.       useMemo(() => {
    
  1387.         useMemoCount++;
    
  1388.       }, []);
    
  1389.       return <div />;
    
  1390.     }
    
  1391. 
    
  1392.     useMemoCount = 0;
    
  1393.     ReactTestRenderer.create(
    
  1394.       <StrictMode>
    
  1395.         <BadUseMemo />
    
  1396.       </StrictMode>,
    
  1397.     );
    
  1398.     expect(useMemoCount).toBe(__DEV__ ? 2 : 1); // Has Hooks
    
  1399.   });
    
  1400. 
    
  1401.   describe('hook ordering', () => {
    
  1402.     const useCallbackHelper = () => React.useCallback(() => {}, []);
    
  1403.     const useContextHelper = () => React.useContext(React.createContext());
    
  1404.     const useDebugValueHelper = () => React.useDebugValue('abc');
    
  1405.     const useEffectHelper = () => React.useEffect(() => () => {}, []);
    
  1406.     const useImperativeHandleHelper = () => {
    
  1407.       React.useImperativeHandle({current: null}, () => ({}), []);
    
  1408.     };
    
  1409.     const useLayoutEffectHelper = () =>
    
  1410.       React.useLayoutEffect(() => () => {}, []);
    
  1411.     const useMemoHelper = () => React.useMemo(() => 123, []);
    
  1412.     const useReducerHelper = () => React.useReducer((s, a) => a, 0);
    
  1413.     const useRefHelper = () => React.useRef(null);
    
  1414.     const useStateHelper = () => React.useState(0);
    
  1415. 
    
  1416.     // We don't include useImperativeHandleHelper in this set,
    
  1417.     // because it generates an additional warning about the inputs length changing.
    
  1418.     // We test it below with its own test.
    
  1419.     const orderedHooks = [
    
  1420.       useCallbackHelper,
    
  1421.       useContextHelper,
    
  1422.       useDebugValueHelper,
    
  1423.       useEffectHelper,
    
  1424.       useLayoutEffectHelper,
    
  1425.       useMemoHelper,
    
  1426.       useReducerHelper,
    
  1427.       useRefHelper,
    
  1428.       useStateHelper,
    
  1429.     ];
    
  1430. 
    
  1431.     // We don't include useContext or useDebugValue in this set,
    
  1432.     // because they aren't added to the hooks list and so won't throw.
    
  1433.     const hooksInList = [
    
  1434.       useCallbackHelper,
    
  1435.       useEffectHelper,
    
  1436.       useImperativeHandleHelper,
    
  1437.       useLayoutEffectHelper,
    
  1438.       useMemoHelper,
    
  1439.       useReducerHelper,
    
  1440.       useRefHelper,
    
  1441.       useStateHelper,
    
  1442.     ];
    
  1443. 
    
  1444.     if (__EXPERIMENTAL__) {
    
  1445.       const useTransitionHelper = () => React.useTransition();
    
  1446.       const useDeferredValueHelper = () =>
    
  1447.         React.useDeferredValue(0, {timeoutMs: 1000});
    
  1448. 
    
  1449.       orderedHooks.push(useTransitionHelper);
    
  1450.       orderedHooks.push(useDeferredValueHelper);
    
  1451. 
    
  1452.       hooksInList.push(useTransitionHelper);
    
  1453.       hooksInList.push(useDeferredValueHelper);
    
  1454.     }
    
  1455. 
    
  1456.     const formatHookNamesToMatchErrorMessage = (hookNameA, hookNameB) => {
    
  1457.       return `use${hookNameA}${' '.repeat(24 - hookNameA.length)}${
    
  1458.         hookNameB ? `use${hookNameB}` : undefined
    
  1459.       }`;
    
  1460.     };
    
  1461. 
    
  1462.     orderedHooks.forEach((firstHelper, index) => {
    
  1463.       const secondHelper =
    
  1464.         index > 0
    
  1465.           ? orderedHooks[index - 1]
    
  1466.           : orderedHooks[orderedHooks.length - 1];
    
  1467. 
    
  1468.       const hookNameA = firstHelper.name
    
  1469.         .replace('use', '')
    
  1470.         .replace('Helper', '');
    
  1471.       const hookNameB = secondHelper.name
    
  1472.         .replace('use', '')
    
  1473.         .replace('Helper', '');
    
  1474. 
    
  1475.       it(`warns on using differently ordered hooks (${hookNameA}, ${hookNameB}) on subsequent renders`, async () => {
    
  1476.         function App(props) {
    
  1477.           /* eslint-disable no-unused-vars */
    
  1478.           if (props.update) {
    
  1479.             secondHelper();
    
  1480.             firstHelper();
    
  1481.           } else {
    
  1482.             firstHelper();
    
  1483.             secondHelper();
    
  1484.           }
    
  1485.           // This should not appear in the warning message because it occurs after the first mismatch
    
  1486.           useRefHelper();
    
  1487.           return null;
    
  1488.           /* eslint-enable no-unused-vars */
    
  1489.         }
    
  1490.         let root;
    
  1491.         await act(() => {
    
  1492.           root = ReactTestRenderer.create(<App update={false} />);
    
  1493.         });
    
  1494.         await expect(async () => {
    
  1495.           try {
    
  1496.             await act(() => {
    
  1497.               root.update(<App update={true} />);
    
  1498.             });
    
  1499.           } catch (error) {
    
  1500.             // Swapping certain types of hooks will cause runtime errors.
    
  1501.             // This is okay as far as this test is concerned.
    
  1502.             // We just want to verify that warnings are always logged.
    
  1503.           }
    
  1504.         }).toErrorDev([
    
  1505.           'Warning: React has detected a change in the order of Hooks called by App. ' +
    
  1506.             'This will lead to bugs and errors if not fixed. For more information, ' +
    
  1507.             'read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks\n\n' +
    
  1508.             '   Previous render            Next render\n' +
    
  1509.             '   ------------------------------------------------------\n' +
    
  1510.             `1. ${formatHookNamesToMatchErrorMessage(hookNameA, hookNameB)}\n` +
    
  1511.             '   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n' +
    
  1512.             '    in App (at **)',
    
  1513.         ]);
    
  1514. 
    
  1515.         // further warnings for this component are silenced
    
  1516.         try {
    
  1517.           await act(() => {
    
  1518.             root.update(<App update={false} />);
    
  1519.           });
    
  1520.         } catch (error) {
    
  1521.           // Swapping certain types of hooks will cause runtime errors.
    
  1522.           // This is okay as far as this test is concerned.
    
  1523.           // We just want to verify that warnings are always logged.
    
  1524.         }
    
  1525.       });
    
  1526. 
    
  1527.       it(`warns when more hooks (${hookNameA}, ${hookNameB}) are used during update than mount`, async () => {
    
  1528.         function App(props) {
    
  1529.           /* eslint-disable no-unused-vars */
    
  1530.           if (props.update) {
    
  1531.             firstHelper();
    
  1532.             secondHelper();
    
  1533.           } else {
    
  1534.             firstHelper();
    
  1535.           }
    
  1536.           return null;
    
  1537.           /* eslint-enable no-unused-vars */
    
  1538.         }
    
  1539.         let root;
    
  1540.         await act(() => {
    
  1541.           root = ReactTestRenderer.create(<App update={false} />);
    
  1542.         });
    
  1543. 
    
  1544.         await expect(async () => {
    
  1545.           try {
    
  1546.             await act(() => {
    
  1547.               root.update(<App update={true} />);
    
  1548.             });
    
  1549.           } catch (error) {
    
  1550.             // Swapping certain types of hooks will cause runtime errors.
    
  1551.             // This is okay as far as this test is concerned.
    
  1552.             // We just want to verify that warnings are always logged.
    
  1553.           }
    
  1554.         }).toErrorDev([
    
  1555.           'Warning: React has detected a change in the order of Hooks called by App. ' +
    
  1556.             'This will lead to bugs and errors if not fixed. For more information, ' +
    
  1557.             'read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks\n\n' +
    
  1558.             '   Previous render            Next render\n' +
    
  1559.             '   ------------------------------------------------------\n' +
    
  1560.             `1. ${formatHookNamesToMatchErrorMessage(hookNameA, hookNameA)}\n` +
    
  1561.             `2. undefined                  use${hookNameB}\n` +
    
  1562.             '   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n' +
    
  1563.             '    in App (at **)',
    
  1564.         ]);
    
  1565.       });
    
  1566.     });
    
  1567. 
    
  1568.     hooksInList.forEach((firstHelper, index) => {
    
  1569.       const secondHelper =
    
  1570.         index > 0
    
  1571.           ? hooksInList[index - 1]
    
  1572.           : hooksInList[hooksInList.length - 1];
    
  1573. 
    
  1574.       const hookNameA = firstHelper.name
    
  1575.         .replace('use', '')
    
  1576.         .replace('Helper', '');
    
  1577.       const hookNameB = secondHelper.name
    
  1578.         .replace('use', '')
    
  1579.         .replace('Helper', '');
    
  1580. 
    
  1581.       it(`warns when fewer hooks (${hookNameA}, ${hookNameB}) are used during update than mount`, async () => {
    
  1582.         function App(props) {
    
  1583.           /* eslint-disable no-unused-vars */
    
  1584.           if (props.update) {
    
  1585.             firstHelper();
    
  1586.           } else {
    
  1587.             firstHelper();
    
  1588.             secondHelper();
    
  1589.           }
    
  1590.           return null;
    
  1591.           /* eslint-enable no-unused-vars */
    
  1592.         }
    
  1593.         let root;
    
  1594.         await act(() => {
    
  1595.           root = ReactTestRenderer.create(<App update={false} />);
    
  1596.         });
    
  1597. 
    
  1598.         await act(() => {
    
  1599.           expect(() => {
    
  1600.             root.update(<App update={true} />);
    
  1601.           }).toThrow('Rendered fewer hooks than expected. ');
    
  1602.         });
    
  1603.       });
    
  1604.     });
    
  1605. 
    
  1606.     it(
    
  1607.       'warns on using differently ordered hooks ' +
    
  1608.         '(useImperativeHandleHelper, useMemoHelper) on subsequent renders',
    
  1609.       () => {
    
  1610.         function App(props) {
    
  1611.           /* eslint-disable no-unused-vars */
    
  1612.           if (props.update) {
    
  1613.             useMemoHelper();
    
  1614.             useImperativeHandleHelper();
    
  1615.           } else {
    
  1616.             useImperativeHandleHelper();
    
  1617.             useMemoHelper();
    
  1618.           }
    
  1619.           // This should not appear in the warning message because it occurs after the first mismatch
    
  1620.           useRefHelper();
    
  1621.           return null;
    
  1622.           /* eslint-enable no-unused-vars */
    
  1623.         }
    
  1624.         const root = ReactTestRenderer.create(<App update={false} />);
    
  1625.         expect(() => {
    
  1626.           try {
    
  1627.             root.update(<App update={true} />);
    
  1628.           } catch (error) {
    
  1629.             // Swapping certain types of hooks will cause runtime errors.
    
  1630.             // This is okay as far as this test is concerned.
    
  1631.             // We just want to verify that warnings are always logged.
    
  1632.           }
    
  1633.         }).toErrorDev([
    
  1634.           'Warning: React has detected a change in the order of Hooks called by App. ' +
    
  1635.             'This will lead to bugs and errors if not fixed. For more information, ' +
    
  1636.             'read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks\n\n' +
    
  1637.             '   Previous render            Next render\n' +
    
  1638.             '   ------------------------------------------------------\n' +
    
  1639.             `1. ${formatHookNamesToMatchErrorMessage(
    
  1640.               'ImperativeHandle',
    
  1641.               'Memo',
    
  1642.             )}\n` +
    
  1643.             '   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n' +
    
  1644.             '    in App (at **)',
    
  1645.         ]);
    
  1646. 
    
  1647.         // further warnings for this component are silenced
    
  1648.         root.update(<App update={false} />);
    
  1649.       },
    
  1650.     );
    
  1651. 
    
  1652.     it('detects a bad hook order even if the component throws', () => {
    
  1653.       const {useState, useReducer} = React;
    
  1654.       function useCustomHook() {
    
  1655.         useState(0);
    
  1656.       }
    
  1657.       function App(props) {
    
  1658.         /* eslint-disable no-unused-vars */
    
  1659.         if (props.update) {
    
  1660.           useCustomHook();
    
  1661.           useReducer((s, a) => a, 0);
    
  1662.           throw new Error('custom error');
    
  1663.         } else {
    
  1664.           useReducer((s, a) => a, 0);
    
  1665.           useCustomHook();
    
  1666.         }
    
  1667.         return null;
    
  1668.         /* eslint-enable no-unused-vars */
    
  1669.       }
    
  1670.       const root = ReactTestRenderer.create(<App update={false} />);
    
  1671.       expect(() => {
    
  1672.         expect(() => root.update(<App update={true} />)).toThrow(
    
  1673.           'custom error',
    
  1674.         );
    
  1675.       }).toErrorDev([
    
  1676.         'Warning: React has detected a change in the order of Hooks called by App. ' +
    
  1677.           'This will lead to bugs and errors if not fixed. For more information, ' +
    
  1678.           'read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks\n\n' +
    
  1679.           '   Previous render            Next render\n' +
    
  1680.           '   ------------------------------------------------------\n' +
    
  1681.           '1. useReducer                 useState\n' +
    
  1682.           '   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n',
    
  1683.       ]);
    
  1684.     });
    
  1685.   });
    
  1686. 
    
  1687.   // Regression test for #14674
    
  1688.   it('does not swallow original error when updating another component in render phase', async () => {
    
  1689.     const {useState} = React;
    
  1690.     spyOnDev(console, 'error').mockImplementation(() => {});
    
  1691. 
    
  1692.     let _setState;
    
  1693.     function A() {
    
  1694.       const [, setState] = useState(0);
    
  1695.       _setState = setState;
    
  1696.       return null;
    
  1697.     }
    
  1698. 
    
  1699.     function B() {
    
  1700.       _setState(() => {
    
  1701.         throw new Error('Hello');
    
  1702.       });
    
  1703.       return null;
    
  1704.     }
    
  1705. 
    
  1706.     expect(() => {
    
  1707.       ReactTestRenderer.create(
    
  1708.         <>
    
  1709.           <A />
    
  1710.           <B />
    
  1711.         </>,
    
  1712.       );
    
  1713.     }).toThrow('Hello');
    
  1714. 
    
  1715.     if (__DEV__) {
    
  1716.       expect(console.error).toHaveBeenCalledTimes(2);
    
  1717.       expect(console.error.mock.calls[0][0]).toContain(
    
  1718.         'Warning: Cannot update a component (`%s`) while rendering ' +
    
  1719.           'a different component (`%s`).',
    
  1720.       );
    
  1721.     }
    
  1722.   });
    
  1723. 
    
  1724.   // Regression test for https://github.com/facebook/react/issues/15057
    
  1725.   it('does not fire a false positive warning when previous effect unmounts the component', async () => {
    
  1726.     const {useState, useEffect} = React;
    
  1727.     let globalListener;
    
  1728. 
    
  1729.     function A() {
    
  1730.       const [show, setShow] = useState(true);
    
  1731.       function hideMe() {
    
  1732.         setShow(false);
    
  1733.       }
    
  1734.       return show ? <B hideMe={hideMe} /> : null;
    
  1735.     }
    
  1736. 
    
  1737.     function B(props) {
    
  1738.       return <C {...props} />;
    
  1739.     }
    
  1740. 
    
  1741.     function C({hideMe}) {
    
  1742.       const [, setState] = useState();
    
  1743. 
    
  1744.       useEffect(() => {
    
  1745.         let isStale = false;
    
  1746. 
    
  1747.         globalListener = () => {
    
  1748.           if (!isStale) {
    
  1749.             setState('hello');
    
  1750.           }
    
  1751.         };
    
  1752. 
    
  1753.         return () => {
    
  1754.           isStale = true;
    
  1755.           hideMe();
    
  1756.         };
    
  1757.       });
    
  1758.       return null;
    
  1759.     }
    
  1760. 
    
  1761.     await act(() => {
    
  1762.       ReactTestRenderer.create(<A />);
    
  1763.     });
    
  1764. 
    
  1765.     expect(() => {
    
  1766.       globalListener();
    
  1767.       globalListener();
    
  1768.     }).toErrorDev([
    
  1769.       'An update to C inside a test was not wrapped in act',
    
  1770.       'An update to C inside a test was not wrapped in act',
    
  1771.       // Note: should *not* warn about updates on unmounted component.
    
  1772.       // Because there's no way for component to know it got unmounted.
    
  1773.     ]);
    
  1774.   });
    
  1775. 
    
  1776.   // Regression test for https://github.com/facebook/react/issues/14790
    
  1777.   it('does not fire a false positive warning when suspending memo', async () => {
    
  1778.     const {Suspense, useState} = React;
    
  1779. 
    
  1780.     let wasSuspended = false;
    
  1781.     function trySuspend() {
    
  1782.       if (!wasSuspended) {
    
  1783.         throw new Promise(resolve => {
    
  1784.           wasSuspended = true;
    
  1785.           resolve();
    
  1786.         });
    
  1787.       }
    
  1788.     }
    
  1789. 
    
  1790.     function Child() {
    
  1791.       useState();
    
  1792.       trySuspend();
    
  1793.       return 'hello';
    
  1794.     }
    
  1795. 
    
  1796.     const Wrapper = React.memo(Child);
    
  1797.     const root = ReactTestRenderer.create(
    
  1798.       <Suspense fallback="loading">
    
  1799.         <Wrapper />
    
  1800.       </Suspense>,
    
  1801.     );
    
  1802.     expect(root).toMatchRenderedOutput('loading');
    
  1803.     await Promise.resolve();
    
  1804.     await waitForAll([]);
    
  1805.     expect(root).toMatchRenderedOutput('hello');
    
  1806.   });
    
  1807. 
    
  1808.   // Regression test for https://github.com/facebook/react/issues/14790
    
  1809.   it('does not fire a false positive warning when suspending forwardRef', async () => {
    
  1810.     const {Suspense, useState} = React;
    
  1811. 
    
  1812.     let wasSuspended = false;
    
  1813.     function trySuspend() {
    
  1814.       if (!wasSuspended) {
    
  1815.         throw new Promise(resolve => {
    
  1816.           wasSuspended = true;
    
  1817.           resolve();
    
  1818.         });
    
  1819.       }
    
  1820.     }
    
  1821. 
    
  1822.     function render(props, ref) {
    
  1823.       useState();
    
  1824.       trySuspend();
    
  1825.       return 'hello';
    
  1826.     }
    
  1827. 
    
  1828.     const Wrapper = React.forwardRef(render);
    
  1829.     const root = ReactTestRenderer.create(
    
  1830.       <Suspense fallback="loading">
    
  1831.         <Wrapper />
    
  1832.       </Suspense>,
    
  1833.     );
    
  1834.     expect(root).toMatchRenderedOutput('loading');
    
  1835.     await Promise.resolve();
    
  1836.     await waitForAll([]);
    
  1837.     expect(root).toMatchRenderedOutput('hello');
    
  1838.   });
    
  1839. 
    
  1840.   // Regression test for https://github.com/facebook/react/issues/14790
    
  1841.   it('does not fire a false positive warning when suspending memo(forwardRef)', async () => {
    
  1842.     const {Suspense, useState} = React;
    
  1843. 
    
  1844.     let wasSuspended = false;
    
  1845.     function trySuspend() {
    
  1846.       if (!wasSuspended) {
    
  1847.         throw new Promise(resolve => {
    
  1848.           wasSuspended = true;
    
  1849.           resolve();
    
  1850.         });
    
  1851.       }
    
  1852.     }
    
  1853. 
    
  1854.     function render(props, ref) {
    
  1855.       useState();
    
  1856.       trySuspend();
    
  1857.       return 'hello';
    
  1858.     }
    
  1859. 
    
  1860.     const Wrapper = React.memo(React.forwardRef(render));
    
  1861.     const root = ReactTestRenderer.create(
    
  1862.       <Suspense fallback="loading">
    
  1863.         <Wrapper />
    
  1864.       </Suspense>,
    
  1865.     );
    
  1866.     expect(root).toMatchRenderedOutput('loading');
    
  1867.     await Promise.resolve();
    
  1868.     await waitForAll([]);
    
  1869.     expect(root).toMatchRenderedOutput('hello');
    
  1870.   });
    
  1871. 
    
  1872.   // Regression test for https://github.com/facebook/react/issues/15732
    
  1873.   it('resets hooks when an error is thrown in the middle of a list of hooks', async () => {
    
  1874.     const {useEffect, useState} = React;
    
  1875. 
    
  1876.     class ErrorBoundary extends React.Component {
    
  1877.       state = {hasError: false};
    
  1878. 
    
  1879.       static getDerivedStateFromError() {
    
  1880.         return {hasError: true};
    
  1881.       }
    
  1882. 
    
  1883.       render() {
    
  1884.         return (
    
  1885.           <Wrapper>
    
  1886.             {this.state.hasError ? 'Error!' : this.props.children}
    
  1887.           </Wrapper>
    
  1888.         );
    
  1889.       }
    
  1890.     }
    
  1891. 
    
  1892.     function Wrapper({children}) {
    
  1893.       return children;
    
  1894.     }
    
  1895. 
    
  1896.     let setShouldThrow;
    
  1897.     function Thrower() {
    
  1898.       const [shouldThrow, _setShouldThrow] = useState(false);
    
  1899.       setShouldThrow = _setShouldThrow;
    
  1900. 
    
  1901.       if (shouldThrow) {
    
  1902.         throw new Error('Throw!');
    
  1903.       }
    
  1904. 
    
  1905.       useEffect(() => {}, []);
    
  1906. 
    
  1907.       return 'Throw!';
    
  1908.     }
    
  1909. 
    
  1910.     let root;
    
  1911.     await act(() => {
    
  1912.       root = ReactTestRenderer.create(
    
  1913.         <ErrorBoundary>
    
  1914.           <Thrower />
    
  1915.         </ErrorBoundary>,
    
  1916.       );
    
  1917.     });
    
  1918. 
    
  1919.     expect(root).toMatchRenderedOutput('Throw!');
    
  1920.     await act(() => setShouldThrow(true));
    
  1921.     expect(root).toMatchRenderedOutput('Error!');
    
  1922.   });
    
  1923. });