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

Svelte中实现两个Writable Store双向同步的方案

问题描述

Svelte中能否让两个Writable Store互相订阅实现双向同步?

存在两个数据匹配但结构不同的对象:

  • Team对象以团队ID作为键,值包含name和users对象;
  • users对象以用户ID为键,且不同团队的用户ID不重复。

需求是创建包含所有用户的userDict对象,实现修改teamDict时userDict同步更新,修改userDict时teamDict也同步更新。

尝试通过subscribe函数实现双向更新时陷入无限循环,初始示例代码如下:

<script>
    import {writable} from "svelte/store";
    
    const teamDict = writable({})
    const userDict = writable({})

    function initTeamDict() {
        teamDict.set({
            1: {
                name: "good team",
                users: {
                    1: "James",
                    2: "Poppy",
                    48: "Hway"
                }
            },
            2: {
                name: "bad team",
                users: {
                    47: "Kelin",
                    35: "Teo",
                    24: "Ferma"
                }
            }
        })
    }

    function initUserDict() {        
        userDict.set(Object.values($teamDict).reduce((acc, team) => ({...acc, ...team[`users`]}), {}))
    }
</script>

<button on:click={initTeamDict}>init team dict</button>
<button on:click={initUserDict}>init user dict</button>

<div> {JSON.stringify($teamDict)}</div>
<div> {JSON.stringify($userDict)}</div>

<button on:click={() => $teamDict[`1`][`users`][`1`] = "top"}>this button should change userDict also </button>
<button on:click={() => $userDict[`1`] = "bottom"}>this button should change teamDict also </button>
解决方案

通过复制对象修改避免触发订阅循环,并用derived创建统一视图解决渲染顺序问题,最终代码如下:

<script>
    import {derived, writable} from "svelte/store";
    import {Button} from "flowbite-svelte";

    const teamDict = writable({})
    const userDict = writable({})

    teamDict.subscribe(($$teamDict) => {
        const userDictCopy = $userDict
        for (const key in userDictCopy) {
            delete userDictCopy[key]
        }
        Object.assign(userDictCopy, Object.values($$teamDict).reduce((acc, team) => ({...acc, ...team[`users`]}), {}))
    })
    userDict.subscribe(($$userDict) => {
        const teamDictCopy = $teamDict
        for (const team of Object.values(teamDictCopy)) {
            team[`users`] = {}
        }
        for (const [userId, user] of Object.entries($$userDict)) {
            teamDictCopy[user[`team_id`]][`users`][userId] = user
        }
    })

    const storeView = derived(
        [teamDict, userDict],
        ([$teamDict, $userDict], set) => {
            set({teamDict: $teamDict, userDict: $userDict})
        }
    )

    function initTeamDict() {
        teamDict.set({
            1: {
                name: "good team",
                users: {
                    1: {
                        "name": "James",
                        "team_id": 1
                    },
                    2: {
                        "name": "Poppy",
                        "team_id": 1
                    },
                    48: {
                        "name": "Hway",
                        "team_id": 1
                    }
                }
            },
            2: {
                name: "bad team",
                users: {
                    47: {
                        "name": "Kelin",
                        "team_id": 2
                    },
                    35: {
                        "name": "Teo",
                        "team_id": 2
                    },
                    24: {
                        "name": "Ferma",
                        "team_id": 2
                    }
                }
            }
        })
    }
</script>

<Button on:click={initTeamDict}>init team dict</Button>

<div> {JSON.stringify($storeView.teamDict)}</div>
<div> {JSON.stringify($storeView.userDict)}</div>

<Button on:click={() => $teamDict[`1`][`users`][`1`][`name`] = "top"}>this button should change userDict also </Button>
<Button on:click={() => $userDict[`1`][`name`] = "bottom"}>this button should change teamDict also </Button>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 16:15:55