Svelte中如何跳过{@attach}钩子内$effect的首次调用?
如何在Svelte的{@attach}钩子中跳过$effect的首次调用?
问题描述
我尝试了以下写法,本以为可行:
<script> let count = $state(0); </script> <button onclick={() => count += 1}> clicks: {count} </button> <div {@attach div => { let firstRun = true; $effect(() => { if (firstRun) { firstRun = false; return; } div.innerText = count * 2; }) }}> </div>
但代码无法识别firstRun的修改,始终触发提前退出。我觉得这像是Svelte的bug,但需要找到解决办法。
当我将firstRun改为$state变量后,虽然能跳过首次执行,但挂载时$effect会执行两次,即便使用untrack修改firstRun也无法解决该问题。
请问有没有可行的解决方法?
解决方案
方法一:使用$state.local定义局部状态
你可以用$state.local()来声明firstRun,它属于作用域内的局部状态,不会触发组件整体更新,同时能让$effect正确感知到变量变化,既不会一直跳过执行,也不会导致$effect重复触发:
<script> let count = $state(0); </script> <button onclick={() => count += 1}> clicks: {count} </button> <div {@attach div => { let firstRun = $state.local(true); $effect(() => { if (firstRun) { firstRun = false; return; } div.innerText = count * 2; }) }}> </div>
原理:$state.local创建的状态仅在当前{@attach}回调的作用域内生效,修改它不会触发组件重新渲染,因此不会导致$effect额外执行;而普通let变量会被$effect捕获初始值,后续修改无法被跟踪,所以始终处于true状态。
方法二:利用$effect的清理函数(备选)
如果你不想使用状态变量,也可以借助$effect的清理函数来标记首次执行完成,不过这种方式依赖清理时机,可靠性略逊于第一种方法:
<script> let count = $state(0); </script> <button onclick={() => count += 1}> clicks: {count} </button> <div {@attach div => { let firstRun = true; $effect(() => { if (!firstRun) { div.innerText = count * 2; } return () => { firstRun = false; }; }) }}> </div>
来源说明
内容的提问来源于stack exchange,提问作者munHunger
相关产品推荐
相关产品推荐

