Svelte向数组头部添加元素导致子组件模态框状态异常
Svelte数组头部添加元素导致模态框内容异常问题
我在Svelte中维护了一个数组,会向数组头部添加元素。当用户打开数组生成的组件绑定的模态框后,添加新元素会让已打开的模态框显示错误内容。
比如初始数组是[3,2,1],生成3个按钮,点击可打开对应模态框;如果此时打开某个模态框,再把数组更新为[4,3,2,1],已打开的模态框会显示原位置被替换后的元素内容。我需要保持当前数组更新方式的同时,避免模态框内容变更。
核心问题:向数组头部添加元素会破坏子组件模态框的状态
相关代码
Each块代码
{#each items as item} <MakeItem item={item}></MakeItem> {/each}
MakeItem组件代码
<script> import Modal from './Modal.svelte'; export let item let showModal = false; </script> <button on:click={() => (showModal = true)}> show modal {item} </button> <Modal bind:showModal> item: {item} </Modal>
Modal组件代码
<script> export let showModal; // boolean let dialog; // HTMLDialogElement $: if (dialog && showModal) dialog.showModal(); </script> <!-- svelte-ignore a11y-click-events-have-key-events --> <dialog bind:this={dialog} on:close={() => (showModal = false)} on:click|self={() => dialog.close()} > <div on:click|stopPropagation> <slot name="header" /> <hr /> <slot /> <hr /> <!-- svelte-ignore a11y-autofocus --> <button autofocus on:click={() => dialog.close()}>close modal</button> </div> </dialog> <style> dialog { max-width: 32em; border-radius: 0.2em; border: none; padding: 0; } dialog::backdrop { background: rgba(0, 0, 0, 0.3); } dialog > div { padding: 1em; } dialog[open] { animation: zoom 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); } @keyframes zoom { from { transform: scale(0.95); } to { transform: scale(1); } } dialog[open]::backdrop { animation: fade 0.2s ease-out; } @keyframes fade { from { opacity: 0; } to { opacity: 1; } } button { display: block; } </style>
解决方案
问题本质是Svelte的{#each}默认以索引作为组件实例的标识,当向数组头部添加元素时,原有元素的索引全部后移,{#each}会复用原来的组件实例并更新item属性,导致已打开的模态框内容被替换。
方法一:为{#each}指定唯一键值
给{#each}添加唯一键,确保每个数组元素对应独立的组件实例:
{#each items as item (item)} <MakeItem item={item}></MakeItem> {/each}
如果数组元素是对象,需使用对象的唯一标识字段(如item.id)作为键。这样添加新元素时,Svelte会创建新的组件实例,原有组件实例的状态不受影响,模态框内容保持正确。
方法二:在子组件中缓存本地状态
在MakeItem组件内部将传入的item保存为本地状态,避免外部更新影响模态框内容:
<script> import Modal from './Modal.svelte'; export let item; let localItem = item; // 缓存初始item值 let showModal = false; </script> <button on:click={() => (showModal = true)}> show modal {item} </button> <Modal bind:showModal> item: {localItem} <!-- 显示本地缓存的值 --> </Modal>
这种方式即使{#each}复用组件实例,模态框展示的是缓存的初始值,不会随外部item变化而改变。
推荐使用方法一,它更贴合Svelte的组件复用逻辑,同时保证状态映射的准确性。
内容的提问来源于stack exchange,提问作者Brandon Kauffman
相关产品推荐
相关产品推荐

