13
let Scheduler;
14
let act;
15
let assertLog;
16
+let useMemo;
17
let useState;
18
let useMemoCache;
19
+let waitForThrow;
20
let MemoCacheSentinel;
21
let ErrorBoundary;
22
29
Scheduler = require('scheduler');
30
act = require('internal-test-utils').act;
31
assertLog = require('internal-test-utils').assertLog;
32
+ useMemo = React.useMemo;
33
useMemoCache = require('react/compiler-runtime').c;
34
useState = React.useState;
35
+ waitForThrow = require('internal-test-utils').waitForThrow;
36
MemoCacheSentinel = Symbol.for('react.memo_cache_sentinel');
37
38
class _ErrorBoundary extends React.Component {
649
</>,
650
);
651
});
652
+
653
+ // @gate enableUseMemoCacheHook
654
+ it('(repro) infinite renders when used with setState during render', async () => {
655
+ // Output of react compiler on `useUserMemo`
656
+ function useCompilerMemo(value) {
657
+ let arr;
658
+ const $ = useMemoCache(2);
659
+ if ($[0] !== value) {
660
+ arr = [value];
661
+ $[0] = value;
662
+ $[1] = arr;
663
+ } else {
664
+ arr = $[1];
665
+ }
666
+ return arr;
667
+ }
668
+
669
+ // Baseline / source code
670
+ function useUserMemo(value) {
671
+ return useMemo(() => [value], [value]);
672
+ }
673
+
674
+ function makeComponent(hook) {
675
+ return function Component({value}) {
676
+ const state = hook(value);
677
+ const [prevState, setPrevState] = useState(null);
678
+ if (state !== prevState) {
679
+ setPrevState(state);
680
+ }
681
+ return <div>{state.join(',')}</div>;
682
+ };
683
+ }
684
+
685
+ /**
686
+ * Test case: note that the initial render never completes
687
+ */
688
+ let root = ReactNoop.createRoot();
689
+ const IncorrectInfiniteComponent = makeComponent(useCompilerMemo);
690
+ root.render(<IncorrectInfiniteComponent value={2} />);
691
+ await waitForThrow(
692
+ 'Too many re-renders. React limits the number of renders to prevent ' +
693
+ 'an infinite loop.',
694
+ );
695
+
696
+ /**
697
+ * Baseline test: initial render is expected to complete after a retry
698
+ * (triggered by the setState)
699
+ */
700
+ root = ReactNoop.createRoot();
701
+ const CorrectComponent = makeComponent(useUserMemo);
702
+ await act(() => {
703
+ root.render(<CorrectComponent value={2} />);
704
+ });
705
+ expect(root).toMatchRenderedOutput(<div>2</div>);
706
+ });
707
});