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
相关产品推荐
相关产品推荐

