Don't ignore dependencies for render phase update (#16574)
Dan Abramov committed
Aug 28, 2019 at 16:55 UTC
01fb68b9bf680ab8bbf96e86501e0fc540b3cc97
2 files changed
+48
packages/react-reconciler/src/ReactFiberHooks.js
+5
@@ -428,6 +428,11 @@ export function renderWithHooks(
428
do {
429
didScheduleRenderPhaseUpdate = false;
430
numberOfReRenders += 1;
431
+ if (__DEV__) {
432
+ // Even when hot reloading, allow dependencies to stabilize
433
+ // after first render to prevent infinite render phase updates.
434
+ ignorePreviousDependencies = false;
435
+ }
436
437
// Start over from the beginning of the list
438
nextCurrentHook = current !== null ? current.memoizedState : null;
packages/react-refresh/src/__tests__/ReactFresh-test.js
+43
@@ -2350,6 +2350,49 @@ describe('ReactFresh', () => {
2350
}
2351
});
2352
2353
+ // This pattern is inspired by useSubscription and similar mechanisms.
2354
+ it('does not get into infinite loops during render phase updates', () => {
2355
+ if (__DEV__) {
2356
+ render(() => {
2357
+ function Hello() {
2358
+ const source = React.useMemo(() => ({value: 10}), []);
2359
+ const [state, setState] = React.useState({value: null});
2360
+ if (state !== source) {
2361
+ setState(source);
2362
+ }
2363
+ return <p style={{color: 'blue'}}>{state.value}</p>;
2364
+ }
2365
+ $RefreshReg$(Hello, 'Hello');
2366
+ return Hello;
2367
+ });
2368
+
2369
+ const el = container.firstChild;
2370
+ expect(el.textContent).toBe('10');
2371
+ expect(el.style.color).toBe('blue');
2372
+
2373
+ // Perform a hot update.
2374
+ act(() => {
2375
+ patch(() => {
2376
+ function Hello() {
2377
+ const source = React.useMemo(() => ({value: 20}), []);
2378
+ const [state, setState] = React.useState({value: null});
2379
+ if (state !== source) {
2380
+ // This should perform a single render-phase update.
2381
+ setState(source);
2382
+ }
2383
+ return <p style={{color: 'red'}}>{state.value}</p>;
2384
+ }
2385
+ $RefreshReg$(Hello, 'Hello');
2386
+ return Hello;
2387
+ });
2388
+ });
2389
+
2390
+ expect(container.firstChild).toBe(el);
2391
+ expect(el.textContent).toBe('20');
2392
+ expect(el.style.color).toBe('red');
2393
+ }
2394
+ });
2395
+
2396
it('can hot reload offscreen components', () => {
2397
if (__DEV__) {
2398
const AppV1 = prepare(() => {