SolidJS:从 IntersectionObserver 回调中更新信号
SolidJS: Updating signal from within IntersectionObserver Callback
以下是 solid.js 应用程序的代码片段。我正在尝试从 IntersectionObserver 的回调中更新顶级信号的状态。状态在回调范围内更新,但不在回调范围外。
问题一:这怎么可能?如果我可以访问 setIds()
然后操纵该值 - 什么会阻止此值更新发生在回调范围之外?
问题二:有人知道如何从回调中更新信号状态吗?
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()));
以下内容适用于喜欢在真实场景中进行测试的人:
我还提供了一个包含整个 solid.js 应用程序的代码块,可用于测试。该应用程序在一个最大高度为 ${n} 像素的自动滚动元素中提供了一个包含 1,000 个段落标记的循环 - 创建滚动效果。每个段落都带有一个(n?)个唯一 ID。 我的 objective 有一个不断更新的列表(存储在顶级信号中),我可以用它来确定哪些 ID 是可见的。 到目前为止,我一直尝试使用包含 callback
的 IntersectionObserver
来做到这一点。这在 callback
之内工作,但是, callback
函数的外部 未更新该值。
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);
信号不关心它们在什么范围内,影响与否,所以在交叉路口观察者中是无关紧要的。
从 createSignal
返回的 setter 函数对上一个值和下一个值运行身份检查以避免不必要的更新。
在您的示例中,您没有设置新值,而是将其分配给一个变量,对其进行变异并重新设置。由于Sets是参考值,prev和next值指向同一个对象,ids() === current
returns true,所以信号更新失败。
const [ids, setIds] = createSignal(new Set<string>());
createEffect(() => {
const current = ids();
setIds(current); // Not a new value
});
您需要传递一个新的 Set 来触发更新:
const [ids, setIds] = createSignal<Set<string>>(new Set<string>());
createEffect(() => {
console.log(ids());
});
let i = 0;
setInterval(() => {
setIds(new Set([...ids(), String(i++)]));
}, 1000);
或者您可以通过传递 { equals: false }
强制 re-running 效果,即使没有触发更新:
const [ids, setIds] = createSignal(new Set<string>(), { equals: false });
现在将始终调用效果:
createEffect(() => {
console.log(ids());
});
let i = 0;
setInterval(() => {
ids().add(String(i++));
setIds(ids()); // Not a new value
}, 1000);
以下是 solid.js 应用程序的代码片段。我正在尝试从 IntersectionObserver 的回调中更新顶级信号的状态。状态在回调范围内更新,但不在回调范围外。
问题一:这怎么可能?如果我可以访问 setIds()
然后操纵该值 - 什么会阻止此值更新发生在回调范围之外?
问题二:有人知道如何从回调中更新信号状态吗?
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()));
以下内容适用于喜欢在真实场景中进行测试的人:
我还提供了一个包含整个 solid.js 应用程序的代码块,可用于测试。该应用程序在一个最大高度为 ${n} 像素的自动滚动元素中提供了一个包含 1,000 个段落标记的循环 - 创建滚动效果。每个段落都带有一个(n?)个唯一 ID。 我的 objective 有一个不断更新的列表(存储在顶级信号中),我可以用它来确定哪些 ID 是可见的。 到目前为止,我一直尝试使用包含 callback
的 IntersectionObserver
来做到这一点。这在 callback
之内工作,但是, callback
函数的外部 未更新该值。
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);
信号不关心它们在什么范围内,影响与否,所以在交叉路口观察者中是无关紧要的。
从 createSignal
返回的 setter 函数对上一个值和下一个值运行身份检查以避免不必要的更新。
在您的示例中,您没有设置新值,而是将其分配给一个变量,对其进行变异并重新设置。由于Sets是参考值,prev和next值指向同一个对象,ids() === current
returns true,所以信号更新失败。
const [ids, setIds] = createSignal(new Set<string>());
createEffect(() => {
const current = ids();
setIds(current); // Not a new value
});
您需要传递一个新的 Set 来触发更新:
const [ids, setIds] = createSignal<Set<string>>(new Set<string>());
createEffect(() => {
console.log(ids());
});
let i = 0;
setInterval(() => {
setIds(new Set([...ids(), String(i++)]));
}, 1000);
或者您可以通过传递 { equals: false }
强制 re-running 效果,即使没有触发更新:
const [ids, setIds] = createSignal(new Set<string>(), { equals: false });
现在将始终调用效果:
createEffect(() => {
console.log(ids());
});
let i = 0;
setInterval(() => {
ids().add(String(i++));
setIds(ids()); // Not a new value
}, 1000);