SOLIDJS:从InterSectionObserver回调中更新信号

发布于 2025-01-31 13:19:08 字数 3519 浏览 4 评论 0原文

以下是来自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}像素 - 创建滚动效果。每个段落都带有(n?)唯一的ID。 我的目标是有一个不断的更新列表(存储在最高级信号中),我可以用来确定确切可见的ID。到目前为止,我一直在尝试使用<<代码> InterSectionObserver 包含回调。这在内部工作 回调但是,该值未在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);

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);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

作业与我同在 2025-02-07 13:19:08

信号不在乎他们所处的范围,无论是否效果,因此在十字路口观察者中是无关紧要的。

创建返回的setter函数在预见和下一个值上运行身份检查,以避免不必要的更新。

在您的示例中,您没有设置一个新值,而是将其分配到变量中,将其突变并重新设置。由于集合是参考值,预见和下一个指向同一对象的值,因此ids()===当前返回true,因此信号无法更新。

const [ids, setIds] = createSignal(new Set<string>());

 createEffect(() => {
   const current = ids();
   setIds(current); // Not a new value
 });

您需要传递新集合以触发更新:

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}来强制重新运行效果,即使没有触发更新:

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);

Signals do not care what scope they are in, effect or not, so being in an intersection observer is irrelevant.

The setter function returned from createSignal runs an identity check on the prev and next values to avoid unnecessary updates.

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, prev and next values pointing to the same object, ids() === current returns true, so signal fails to 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);

Or you can force re-running effects by passing { equals: false }, even though update is not triggered:

const [ids, setIds] = createSignal(new Set<string>(), { equals: false });

Now effects will always be called:

createEffect(() => {
  console.log(ids());
});

let i = 0;
setInterval(() => {
  ids().add(String(i++));
  setIds(ids()); // Not a new value
}, 1000);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文