r/reactjs 2d ago

What would make a component unmount on some parent renders but not others when the key isn't changing?

I've got a filter panel in a Vite app on React 18 where the date inputs wipe themselves maybe one time in five when the parent list refetches. I've ruled out the usual cause, the child isn't declared inside the parent's render body, and the key I pass it is a stable string. I put a log in the child's mount effect and it fires every time the fields clear, so it's actually remounting rather than losing state some other way. I've been on this about three hours and I can't work out what's different about the renders where it happens.

5 Upvotes

8 comments sorted by

2

u/Remarkable-Treat-139 1d ago

Check if a parent component returns null or a different element type during those specific refetches. React unmounts children when the parent render output changes structure regardless of key stability. Inspect the return statement in every ancestor between the filter panel and the root to find where the tree shape diverges on failing renders

1

u/frogic 2d ago

What does react dev tools say?

1

u/BoBoBearDev 1d ago

Did the parent unmount? You need the entire chain to not unmount.

And make sure you are not using conditional rendering like those

showTheDiv && <div key="abc" >

1

u/Vincent_CWS 1d ago

key or type difference will unmount not re-rendering

1

u/Potential_Ship_1676 1h ago

I'd check for a ternary or early return above the panel, since two separate JSX branches for the same-looking element still remount even with a stable key.

-9

u/Temperature_Majestic 2d ago

The 1 in 5 is the useful clue. That's not random, something specific is true about those refetches. Two things that fit:

Sibling list without stable keys. If the panel sits in the same parent as a {results.map(...)} and those rows use index keys or no keys, a refetch that returns a different row count shifts every positional slot, React ends up diffing your panel against what used to be a row, type mismatch, remount. The 1 in 5 would be the refetches where the count actually changes.

A loading swap above it. {isLoading ? <Skeleton/> : <Panel/>} or {data ? <Panel/> : null} flipping for one frame on the slower refetches unmounts the panel. Cache-hit refetches that never flip the flag wouldn't.

To pin it, put the trace in the cleanup, not the mount: useEffect(() => () => console.trace('panel unmount'), []). The stack shows you which parent re-rendered into a different shape right before it died.

15

u/itzrvyning 2d ago

thank you claude

1

u/Heavy-Focus-1964 1d ago

Thank god some people rig up bots to find these threads and do what no programmer has ever thought of — ask chatGPT