Skip to content

Commit 01fb68b

Browse files
authored
Don't ignore dependencies for render phase update (#16574)
1 parent 1b585f6 commit 01fb68b

File tree

2 files changed

+48
-0
lines changed

2 files changed

+48
-0
lines changed

packages/react-reconciler/src/ReactFiberHooks.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,11 @@ export function renderWithHooks(
428428
do {
429429
didScheduleRenderPhaseUpdate = false;
430430
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+
}
431436

432437
// Start over from the beginning of the list
433438
nextCurrentHook = current !== null ? current.memoizedState : null;

packages/react-refresh/src/__tests__/ReactFresh-test.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2350,6 +2350,49 @@ describe('ReactFresh', () => {
23502350
}
23512351
});
23522352

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+
23532396
it('can hot reload offscreen components', () => {
23542397
if (__DEV__) {
23552398
const AppV1 = prepare(() => {

0 commit comments

Comments
 (0)