Issue
The following is a code snippet from a solid.js application. I am attempting to update the state of a top-level signal from within an IntersectionObserver’s callback. The state is being updated within the scope of the callback, but not outside.
Question one: how is this possible? If I can access setIds()
and then manipulate the value – what would stop this value update from taking place outside of the scope of the callback?
Question two: does anyone know how I can update the state of the signal from within the callbacK?
const [ids, setIds] = createSignal(new Set<string>());
createEffect(() => {
const callback = (entries: any, observer: any) => {
const current = ids()
entries.forEach((e: any) => {
const intersecting = e.isIntersecting;
const id = e.target.id;
if (intersecting) {
setIds(current.add(id));
}
else {
setIds(current.remove(id));
}
});
// NOTE: The following outputs the correct value
console.log("This is the correct value:", ids())
};
const observer = new IntersectionObserver(callback);
const elements = Array.from(document.querySelectorAll("p"));
elements.forEach((element) => observer.observe(element));
return () => observer.disconnect();
}, [setIds]);
// NOTE: The following does NOT update at all.
createEffect(() => console.log("This value is not being updated:", ids()));
The following is for people that prefer to test things out in real-world scenarios:
I have also provided a code block with an entire solid.js application that can be used for testing. The app provides a loop with 1,000 paragraph tags within an autoscroll element with a max height of ${n} pixels – creating a scrolling effect. Each paragraph comes with a(n?) unique id. It is my objective to have a constantly updating list (stored in a top level signal) that i can use to determine exactly which id’s are visible. So far, I have been trying to do this with an IntersectionObserver
containing a callback
. This is working within the callback
however, the value is not being updated outside of the callback
function.
import {
For,
createSignal,
createEffect,
} from "solid-js";
import { render } from 'solid-js/web';
const App = () => {
const [ids, setIds] = createSignal(new Set<string>());
createEffect(() => {
const callback = (entries: any, observer: any) => {
const current = ids()
entries.forEach((e: any) => {
const intersecting = e.isIntersecting;
const id = e.target.id;
if (intersecting) {
setIds(current.add(id));
}
else {
setIds(current.remove(id));
}
});
// NOTE: The following outputs the correct value
console.log("This is the correct value:", ids())
};
let options = {
root: document.getElementById("main"),
threshold: 1.0
}
const observer = new IntersectionObserver(callback, options);
const elements = Array.from(document.querySelectorAll("p"));
elements.forEach((element) => observer.observe(element));
return () => observer.disconnect();
}, [setIds]);
// NOTE: The following does NOT update at all.
createEffect(() => console.log("This value is not being updated:", ids()));
const arr = Array.from(Array(1000).keys())
return (
<div id="main" class="overflow-auto" style="max-height: 550px;">
<For each={arr}>
{(number) => {
return (
<p id={`${number}`}>
{number}
</p>
)
}}
</For>
</div>
);
};
render(() => <App />, document.getElementById('root') as HTMLElement);
Solution
Signals do not care what scope they are in, effect or not, so being in an intersection observer is irrelevant.
Signals are atomic values and you need to set a new value to trigger update. In your example you are not setting a new value but assigning it into a variable, mutating it and setting it back. Since Sets are reference values, basically you are not setting a new value, hence you don’t trigger an update:
const [ids, setIds] = createSignal(new Set<string>());
createEffect(() => {
const current = ids();
setIds(current); // Not a new value
});
You need to pass a new Set to trigger update:
const [ids, setIds] = createSignal<Set<string>>(new Set<string>());
createEffect(() => {
console.log(ids());
});
let i = 0;
setInterval(() => {
setIds(new Set([...ids(), String(i++)]));
}, 1000);
Answered By – snnsnn
This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0