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

如何让回调中从Store获取的modal具备响应式特性?

问题

我尝试让回调中返回的modal具备响应式特性,是否有可行方案?例如当我调用modal.isOpen = false时,state数组中的底层模型并未更新。


原代码

Store:

const store = () => {
    const state = [];
    const { subscribe, set, update } = writable(state)

    const openModal(data) {
        const modal = {
            isOpen: false,
            loading: false,
            onConfirm: data.onConfirm
        }
        return [...state, modal]
    }

    return {
        subscribe,
        set,
        update,
        openModal
    }
}
{#each $modals as modal, i (modal)}
    <Modal bind:open={$modals[i].isOpen}>
        <Button
            on:click={() => $modals[i].onConfirm($modals[i])}
            bind:loading={$modals[i].loading}>
            Close
        </Button>
    </Modal>
{/each}

调用组件:

store.openModel({
    onConfirm: async ($modal) => {
        $modal.loading = true
        await doSomething()
        $modal.loading = false
        $modal.isOpen = false
    }
})

解决方案

核心问题

直接修改数组内对象的属性(比如$modal.isOpen = false)不会触发Svelte store的响应式更新,因为writable store仅在通过set/update方法修改整个state引用时才会通知订阅者,直接修改对象属性不会改变数组的引用,store无法感知变化。

基础修复方案

1. 修正Store的openModal方法

原方法只是返回新数组但未更新store状态,需要改用update方法触发store更新:

const store = () => {
    const state = [];
    const { subscribe, set, update } = writable(state)

    const openModal(data) {
        update(currentState => {
            const modal = {
                isOpen: true, // 符合openModal语义,默认打开弹窗
                loading: false,
                onConfirm: data.onConfirm
            }
            return [...currentState, modal]
        })
    }

    return {
        subscribe,
        set,
        update,
        openModal
    }
}

2. 修改回调逻辑,通过store更新状态

不要直接修改modal对象,而是通过update方法生成新数组副本,修改对应索引的modal属性:

{#each $modals as modal, i (modal)}
    <Modal bind:open={$modals[i].isOpen}>
        <Button
            on:click={() => $modals[i].onConfirm(i)} <!-- 传递弹窗索引 -->
            bind:loading={$modals[i].loading}>
            Close
        </Button>
    </Modal>
{/each}
store.openModal({
    onConfirm: async (modalIndex) => {
        // 更新loading状态
        store.update(modals => {
            const updatedModals = [...modals];
            updatedModals[modalIndex].loading = true;
            return updatedModals;
        });

        await doSomething();

        // 重置loading并关闭弹窗
        store.update(modals => {
            const updatedModals = [...modals];
            updatedModals[modalIndex].loading = false;
            updatedModals[modalIndex].isOpen = false;
            return updatedModals;
        });
    }
})

进阶方案:单个Modal独立Store

如果需要更细粒度的响应式控制,可以将每个modal包装成独立的writable store,修改单个modal属性时无需更新整个数组:

const store = () => {
    const state = [];
    const { subscribe, set, update } = writable(state)

    const openModal(data) {
        const modalStore = writable({
            isOpen: true,
            loading: false,
            onConfirm: data.onConfirm
        });
        update(currentState => [...currentState, modalStore]);
        return modalStore;
    }

    return {
        subscribe,
        set,
        update,
        openModal
    }
}

Modal组件调整为:

{#each $modals as modalStore, i (modalStore)}
    <Modal bind:open={$modalStore.isOpen}>
        <Button
            on:click={() => $modalStore.onConfirm($modalStore)}
            bind:loading={$modalStore.loading}>
            Close
        </Button>
    </Modal>
{/each}

调用时可直接修改modal状态:

const modalStore = store.openModal({
    onConfirm: async ($modal) => {
        $modal.loading = true;
        await doSomething();
        $modal.loading = false;
        $modal.isOpen = false;
    }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 00:45:36