main
js 156 lines 4.23 KB
Raw
1 let React;
2 let ReactNoop;
3 let Scheduler;
4 let ContinuousEventPriority;
5 let startTransition;
6 let useState;
7 let useEffect;
8 let act;
9 let waitFor;
10 let waitForPaint;
11 let assertLog;
12
13 describe('ReactUpdatePriority', () => {
14 beforeEach(() => {
15 jest.resetModules();
16
17 React = require('react');
18 ReactNoop = require('react-noop-renderer');
19 Scheduler = require('scheduler');
20 act = require('internal-test-utils').act;
21 ContinuousEventPriority =
22 require('react-reconciler/constants').ContinuousEventPriority;
23 startTransition = React.startTransition;
24 useState = React.useState;
25 useEffect = React.useEffect;
26
27 const InternalTestUtils = require('internal-test-utils');
28 waitFor = InternalTestUtils.waitFor;
29 waitForPaint = InternalTestUtils.waitForPaint;
30 assertLog = InternalTestUtils.assertLog;
31 });
32
33 function Text({text}) {
34 Scheduler.log(text);
35 return text;
36 }
37
38 it('setState inside passive effect triggered by sync update should have default priority', async () => {
39 const root = ReactNoop.createRoot();
40
41 function App() {
42 const [state, setState] = useState(1);
43 useEffect(() => {
44 setState(2);
45 }, []);
46 return <Text text={state} />;
47 }
48
49 await act(() => {
50 ReactNoop.flushSync(() => {
51 root.render(<App />);
52 });
53 // Should not have flushed the effect update yet
54 assertLog([1]);
55 });
56 assertLog([2]);
57 });
58
59 it('setState inside passive effect triggered by idle update should have idle priority', async () => {
60 const root = ReactNoop.createRoot();
61
62 let setDefaultState;
63 function App() {
64 const [idleState, setIdleState] = useState(1);
65 const [defaultState, _setDefaultState] = useState(1);
66 setDefaultState = _setDefaultState;
67 useEffect(() => {
68 Scheduler.log('Idle update');
69 setIdleState(2);
70 }, []);
71 return <Text text={`Idle: ${idleState}, Default: ${defaultState}`} />;
72 }
73
74 await act(async () => {
75 ReactNoop.idleUpdates(() => {
76 root.render(<App />);
77 });
78 // Should not have flushed the effect update yet
79 await waitForPaint(['Idle: 1, Default: 1']);
80
81 // Schedule another update at default priority
82 setDefaultState(2);
83
84 if (gate(flags => flags.enableYieldingBeforePassive)) {
85 // The default update flushes first, because
86 await waitForPaint([
87 // Idle update is scheduled
88 'Idle update',
89 ]);
90 await waitForPaint([
91 // The default update flushes first, without including the idle update
92 'Idle: 1, Default: 2',
93 ]);
94 } else {
95 // The default update flushes first, because
96 await waitForPaint([
97 // Idle update is scheduled
98 'Idle update',
99
100 // The default update flushes first, without including the idle update
101 'Idle: 1, Default: 2',
102 ]);
103 }
104 });
105 // Now the idle update has flushed
106 assertLog(['Idle: 2, Default: 2']);
107 });
108
109 it('continuous updates should interrupt transitions', async () => {
110 const root = ReactNoop.createRoot();
111
112 let setCounter;
113 let setIsHidden;
114 function App() {
115 const [counter, _setCounter] = useState(1);
116 const [isHidden, _setIsHidden] = useState(false);
117 setCounter = _setCounter;
118 setIsHidden = _setIsHidden;
119 if (isHidden) {
120 return <Text text={'(hidden)'} />;
121 }
122 return (
123 <>
124 <Text text={'A' + counter} />
125 <Text text={'B' + counter} />
126 <Text text={'C' + counter} />
127 </>
128 );
129 }
130
131 await act(() => {
132 root.render(<App />);
133 });
134 assertLog(['A1', 'B1', 'C1']);
135 expect(root).toMatchRenderedOutput('A1B1C1');
136
137 await act(async () => {
138 startTransition(() => {
139 setCounter(2);
140 });
141 await waitFor(['A2']);
142 ReactNoop.unstable_runWithPriority(ContinuousEventPriority, () => {
143 setIsHidden(true);
144 });
145 });
146 assertLog([
147 // Because the hide update has continuous priority, it should interrupt the
148 // in-progress transition
149 '(hidden)',
150 // When the transition resumes, it's a no-op because the children are
151 // now hidden.
152 '(hidden)',
153 ]);
154 expect(root).toMatchRenderedOutput('(hidden)');
155 });
156 });