-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unexpected null ref #4049
Comments
Ah, yeah. This is a bug in the ref "unset" code. When I switch to function refs, I can see the following ref updates: // phase 1
assigning twoRef <div>Element two</div>
assigning threeRef <div>Element three</div>
// phase 2
assigning twoRef <div>Element two</div>
assigning threeRef <div>Element three</div>
assigning twoRef null // <--- this the bug
assigning oneRef <div>Element one</div> |
I guess the unsetting code needs to check that it's unsetting the value it set. |
Hmm, I have investigated this issue before while working on v11. Basically the issue stems from the parent indentation being introduced. We recurse down the tree initially and see the following
Notice the depth annotation as this will play a role here, when we go into the second phase we see the following
So while we go through the tree we'll end up at depth 3 first to start performing operations, we see that I Acknowledge that this is a very peculiar scenario and we might be able to circumvent this by first going through the tree and registering stuff for unmount and queueing the new refs to apply after. |
Yeah I think this basically requires doing all ref updates in commit. To do that we need to expand the definition of what we store in the commit queue to include different functions (basically opcodes). |
The concern I have are layout and normal effects we need to guarantee refs being present 😅 I think storing it in the commit queue will indeed fix this |
Here's a little workaround in case people need it: import { useRef } from "preact/hooks";
const modifiedThisPass = new WeakSet();
const shittyProxies = new WeakMap();
export function shittyElRef() {
const ref = useRef(null);
if (!shittyProxies.has(ref)) {
shittyProxies.set(ref, {
get current() {
return ref.current;
},
set current(val) {
if (modifiedThisPass.has(ref)) {
// If this has already been set this pass,
// ignore null values
if (val === null) return;
} else {
modifiedThisPass.add(ref);
queueMicrotask(() => modifiedThisPass.delete(ref));
}
ref.current = val;
},
});
}
return shittyProxies.get(ref);
} It tries to ensure that refs can't be set from a value to |
@jakearchibald this should be fixed in 10.16.0, thank you so much again for the reproduction! It helped a ton in identifying this! |
Thanks for fixing it so quickly! |
Describe the bug
A ref is unexpectedly null when using a particular JSX structure.
To Reproduce
Demo.
View the console. I see:
Expected behaviour
It's expected that
oneRef
isnull
in phase 1 (the element isn't rendered), buttwoRef
shouldn't benull
in phase 2, as the element is rendered.The bug seems to depend on a few things:
If
<div class="wrapper">
is switched to a fragment, the bug still happens. However, if<div class="wrapper">
is removed, the bug does not happen.If
<div class="outer">
is switched to a fragment, the bug does not happen.The text was updated successfully, but these errors were encountered: