Possible Bug: interplay between reconcilliation algorithm and DOM manipulations via refs #20891
Labels
Resolution: Stale
Automatically closed due to inactivity
Status: Unconfirmed
A potential issue that we haven't yet confirmed as a bug
Introduction
I'm working with a library that performs DOM manipulations via
ref
s inuseEffect
s after React has rendered.Background
My understanding of the reconciliation algorithm is that it compares subsequently rendered virtual DOMs and only perform the necessary updates on the real DOM when necessary according to this comparison. For this reason, the following example will continue to show a
div
with a green background throughout subsequent rerenderings, even though therender
method returns adiv
with a blue background:(Sandbox link: https://codesandbox.io/s/festive-leavitt-n44hs?file=/src/App.js)
React is simply unaware of the change done directly via the
ref
and so the comparison between subsequent virtual DOMs only indicate that thepadding
must be updated.Problem
This is all fine when it comes to properties, but as far as children are concerned, the behaviour is slightly more obscure and inconsistent (perhaps due to children seemingly being something in between a prop on the parent element and distinct other elements?).
Consider the following toy example where a wrapper component performs an update (replaces
0
s with9
s) on the content of its DOM children viaref
s after rendering. Remember that this is a toy example and I know that the results in this example can be acquired in a much simpler (better!) way in React.(Sandbox link: https://codesandbox.io/s/cranky-blackwell-xqbkx?file=/src/App.js)
Depending on the structure of the children, the results differ:
WrapperComponent
s contain the updated number correctly in therender
printout. Then note that in theuseEffect
when inspecting theref
s, React has changed the content of the secondWrapperComponent
in the real DOM to reflect the update in the child components in the virtual DOM whereas the child of the first has not been updated (seepre-alter
printout). As a result, the changes (in the firstWrapperComponent
) are not reflected in the UI and theuseEffect
has nothing new to process.for
loop in theuseEffect
of theWrapperComponent
and uncomment the row below it. Refresh the UI and press the button. With this version, React doesn't update the real DOM at all (can be seen in thepre-alter
printout) even though the children are still correct in therender
printout.key
props in thespan
s and restore thefor
loop inWrapperComponent
and comment out the row below it again. Update the UI and hit the button repeatedly and note that now it works perfectly. React updates the DOM as expected and this is reflected in bothpre-alter
printouts.React version: 17.0
Steps To Reproduce
See previous section
Link to code example:
See previous section
The current behavior
See previous section
The expected behavior
span
s has changed, it is reasonable that React tries to update only that and bails out silently when it can no longer find those nodes in the real DOM. One could reason that React should reinsert them but I still think this behaviour is consistent.WrapperComponent
s and so the old ones are thrown out and the new ones are added. Everything works as desired.WrapperComponent
s only update the content of thespan
s and so React should be able to map content change in the virtual DOMs to these very samespan
s. It manages to do so for the secondWrapperComponent
but not the first, despite the changed content.I would expect this behaviour to be more consistent and not depend on the structure of the children like this. My suspicion is that it seems to be related to how React maps virtual DOM nodes with mixed content (expressions and literal text) to real DOM nodes but I can't see how, where and why. Hopefully this is not a bug in which case I am sorry for wasting your time.
The text was updated successfully, but these errors were encountered: