You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Preact中监听含Set的Signal变化及DOM不更新问题排查

问题

点击按钮后希望修改按钮文本,已点击的按钮ID存储在Signal的Set中,但Set变化时未触发DOM更新,请问哪里操作有误?

import { render } from 'preact';
import { signal, effect } from "@preact/signals-core";


export function App() {

    const selected = signal(new Set<number>);

    const onClick = (index) =>{
        console.log("click" + index +" current state:"+ selected.value.has(index))
        const s = new Set(selected.value)
        if(selected.value.has(index))
            s.delete(index)
        else
            s.add(index)

        selected.value = s
    }


    return (
        <div>
            <div>
            {new Array(10).fill(0).map((_, index) => (
                <button type="button" onClick={() => onClick(index)}>
                    {selected.value.has(index)+""}
                </button>
            ))}
            </div>
        </div>
    );

}

render(<App />, document.getElementById('app'));
解决方案

问题出在你直接读取selected.value.has(index),这种方式无法让Preact追踪Signal的变化。Preact Signals需要通过自动解包机制触发组件更新,直接访问.value不会建立依赖关联,组件自然感知不到Signal的变化。

以下是两种可行的修改方案:

方法一:利用Signal自动解包(推荐)

在组件中直接使用Signal实例而非.value,Preact会自动完成值的解包,并建立依赖追踪。只需修改按钮文本的表达式:

import { render } from 'preact';
import { signal } from "@preact/signals-core";


export function App() {

    const selected = signal(new Set<number>());

    const onClick = (index) =>{
        console.log("click" + index +" current state:"+ selected.value.has(index))
        const s = new Set(selected.value)
        if(selected.value.has(index))
            s.delete(index)
        else
            s.add(index)

        selected.value = s
    }


    return (
        <div>
            <div>
            {new Array(10).fill(0).map((_, index) => (
                <button type="button" onClick={() => onClick(index)}>
                    {/* 直接使用Signal实例,Preact自动解包并追踪变化 */}
                    {selected.has(index) + ""}
                </button>
            ))}
            </div>
        </div>
    );

}

render(<App />, document.getElementById('app'));

方法二:使用useSignal钩子(Preact专属)

如果你是在Preact项目中,更推荐使用@preact/signals包提供的useSignal钩子,它会自动帮组件完成Signal的依赖追踪,此时直接访问.value也能触发更新:

import { render } from 'preact';
import { useSignal } from "@preact/signals";


export function App() {

    const selected = useSignal(new Set<number>());

    const onClick = (index) =>{
        console.log("click" + index +" current state:"+ selected.value.has(index))
        const s = new Set(selected.value)
        if(selected.value.has(index))
            s.delete(index)
        else
            s.add(index)

        selected.value = s
    }


    return (
        <div>
            <div>
            {new Array(10).fill(0).map((_, index) => (
                <button type="button" onClick={() => onClick(index)}>
                    {selected.value.has(index) + ""}
                </button>
            ))}
            </div>
        </div>
    );

}

render(<App />, document.getElementById('app'));

核心说明

  • 使用@preact/signals-core的signal时,必须通过访问Signal实例本身来触发自动解包和依赖追踪,直接读取.value不会让组件感知到变化。
  • useSignal是Preact封装后的钩子,会自动处理组件与Signal的依赖关联,此时直接访问.value也能正常触发DOM更新。

内容的提问来源于stack exchange,提问作者Horst Lemke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 11:00:57