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

