@samitouri / QOS-React-2 / commits / 21d6395a14

Add test case for #16359 (#16371)

Andrew Clark committed Aug 12, 2019 at 19:00 UTC 21d6395a141f1e725cdf707b606b05f079f48b92
2 files changed +64
packages/react-reconciler/src/ReactFiberBeginWork.js
+4
@@ -2937,6 +2937,10 @@ function beginWork(
2937 renderExpirationTime,
2938 );
2939 } else {
2940 + // An update was scheduled on this fiber, but there are no new props
2941 + // nor legacy context. Set this to false. If an update queue or context
2942 + // consumer produces a changed value, it will set this to true. Otherwise,
2943 + // the component will assume the children have not changed and bail out.
2944 didReceiveUpdate = false;
2945 }
2946 } else {
packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.internal.js
+60
@@ -2138,6 +2138,66 @@ describe('ReactHooksWithNoopRenderer', () => {
2138 expect(ReactNoop).toMatchRenderedOutput('2');
2139 });
2140
2141 + // Regression test. Covers a case where an internal state variable
2142 + // (`didReceiveUpdate`) is not reset properly.
2143 + it('state bail out edge case (#16359)', async () => {
2144 + let setCounterA;
2145 + let setCounterB;
2146 +
2147 + function CounterA() {
2148 + const [counter, setCounter] = useState(0);
2149 + setCounterA = setCounter;
2150 + Scheduler.unstable_yieldValue('Render A: ' + counter);
2151 + useEffect(() => {
2152 + Scheduler.unstable_yieldValue('Commit A: ' + counter);
2153 + });
2154 + return counter;
2155 + }
2156 +
2157 + function CounterB() {
2158 + const [counter, setCounter] = useState(0);
2159 + setCounterB = setCounter;
2160 + Scheduler.unstable_yieldValue('Render B: ' + counter);
2161 + useEffect(() => {
2162 + Scheduler.unstable_yieldValue('Commit B: ' + counter);
2163 + });
2164 + return counter;
2165 + }
2166 +
2167 + const root = ReactNoop.createRoot(null);
2168 + await ReactNoop.act(async () => {
2169 + root.render(
2170 + <>
2171 + <CounterA />
2172 + <CounterB />
2173 + </>,
2174 + );
2175 + });
2176 + expect(Scheduler).toHaveYielded([
2177 + 'Render A: 0',
2178 + 'Render B: 0',
2179 + 'Commit A: 0',
2180 + 'Commit B: 0',
2181 + ]);
2182 +
2183 + await ReactNoop.act(async () => {
2184 + setCounterA(1);
2185 +
2186 + // In the same batch, update B twice. To trigger the condition we're
2187 + // testing, the first update is necessary to bypass the early
2188 + // bailout optimization.
2189 + setCounterB(1);
2190 + setCounterB(0);
2191 + });
2192 + expect(Scheduler).toHaveYielded([
2193 + 'Render A: 1',
2194 + 'Render B: 0',
2195 + 'Commit A: 1',
2196 + // B should not fire an effect because the update bailed out
2197 + // 'Commit B: 0',
2198 + ]);
2199 + });
2200 +
2201 it('should update latest rendered reducer when a preceding state receives a render phase update', () => {
2202 // Similar to previous test, except using a preceding render phase update
2203 // instead of new props.