我有一个数据模型,我不能在这个项目中改变。我正在尝试删除并简化下面的示例代码,因此希望这与我试图重新生成的内容仍然有意义。
假设我有两家商店。一家商店存放“容器”,另一家商店存放“物品”--每个商店在整个应用程序中都被独立地用于各种用途。默认情况下,容器只保存项ID。有时,我希望将数据去规范化,以便在某些页面上使用。我使用派生存储并对每个容器对象进行去denormalize,并将它们转换为DenormalizedContainers。
假设我有一个用例,我想要创建一个新的Item对象,然后将它添加到给定的容器中。据我所知,这将导致派生存储更新两次(一次是对项的更改,另一次是对容器的更改),因此,任何使用该派生存储的内容都将更新两次(取决于调用方式)。
为什么这种行为会因subscribe而异于$
而且,虽然这对我来说不是一个大问题,但我只是好奇是否有任何原生的Svelte-y方法来解决这个问题而不改变数据模型(同时仍然能够使用subscribe API?
<script lang="ts">
import { derived, writable } from "svelte/store";
type Container = {
id: string;
itemIds: string[];
};
type Item = {
id: string;
name: string;
};
type DenormalizedContainer = {
id: string;
items: Item[];
};
const containers = writable<Container[]>([]);
const items = writable<Item[]>([]);
const denormalizedContainers = derived(
[containers, items],
([$containers, $items]): DenormalizedContainer[] => {
return $containers.map(({ id, itemIds }) => {
const denormalizedContainer = {
id,
items: itemIds.map((id) =>
$items.find((item) => {
item.id === id;
})
),
};
return denormalizedContainer;
});
}
);
function handleAddContainer() {
const value = Math.random() * 1000;
const newContainer: Container = { id: `new-container-${value}`, itemIds: [] };
containers.set([...$containers, newContainer]);
}
function handleAddItem() {
const value = Math.random() * 1000;
const newItem: Item = { id: `new-id-${value}`, name: `new-name-${value}` };
items.set([...$items, newItem]);
}
function handleAddBoth() {
const value = Math.random() * 1000;
const newItem: Item = { id: `new-id-${value}`, name: `new-name-${value}` };
const newContainer: Container = { id: `new-container-${value}`, itemIds: [newItem.id] };
items.set([...$items, newItem]);
containers.set([...$containers, newContainer]);
}
$: console.log(`$: There are ${$containers.length} containers`);
$: console.log(`$: There are ${$items.length} items`);
$: console.log(`$: There are ${$denormalizedContainers.length} denormalized containers`);
denormalizedContainers.subscribe((newValue) =>
console.log(
`Subscribe: There are ${$denormalizedContainers.length} denormalized containers`
)
);
</script>
<button class="block" on:click={() => handleAddContainer()}>Add container</button>
<button class="block" on:click={() => handleAddItem()}>Add item</button>
<button class="block" on:click={() => handleAddBoth()}>Add container and item</button>结果:
单击每个按钮一次,以下是日志:
页面加载:
Subscribe: There are 0 denormalized containers
$: There are 0 containers
$: There are 0 items
$: There are 0 denormalized containers添加容器:
Subscribe: There are 1 denormalized containers
$: There are 1 containers
$: There are 1 denormalized containers增加项目:
Subscribe: There are 1 denormalized containers
$: There are 1 items
$: There are 1 denormalized containers加两项:
Subscribe: There are 1 denormalized containers
Subscribe: There are 2 denormalized containers
$: There are 2 containers
$: There are 2 items
$: There are 2 denormalized containers通过$处理的更新一直是我想要的,但出于某种原因,手动subscribe API显示了两个更新。我在.svelte文件之外有几个位置,我需要在那里使用subscribe API。
参考文献:
类似于此,但并没有解释为什么$的行为与subscribe不同。
可能是根本的问题?
https://github.com/sveltejs/svelte/issues/6730
可能在3.1/3.2中解释过。微任务将反应性语句捆绑在一起,并在勾尾应用它们。而subscribe则实时处理更新。注意:将我的subscribe代码封装在tick中只会使它使用相同的数据运行两次。
发布于 2022-06-29 19:28:22
如果您查看编译后的输出,可以看到在update上触发了反应性语句,每个事件循环应该只发生一次。
$$self.$$.update = () => {
if ($$self.$$.dirty & /*$containers*/ 256) {
$: console.log(`$: There are ${$containers.length} containers`);
}
if ($$self.$$.dirty & /*$items*/ 128) {
$: console.log(`$: There are ${$items.length} items`);
}
if ($$self.$$.dirty & /*$denormalizedContainers*/ 64) {
$: console.log(`$: There are ${$denormalizedContainers.length} denormalized containers`);
}
};您可以在事件循环中创建自己的绑定更新的方法。例如使用微任务
function debouncedSubscribe(store, callback) {
let key;
return store.subscribe(value => {
key = {};
const currentKey = key;
queueMicrotask(() => {
if (key == currentKey)
callback(value);
});
});
}
debouncedSubscribe(denormalizedContainers, value =>
console.log(
`Subscribe: There are ${value.length} denormalized containers`
)
);没有办法再次取消微任务,所以旧的任务会通过一个键失效。确保只执行最后一个任务的其他方法可能更优雅。
对于setTimeout,也可以这样做,并且可以取消前一个延迟,但是最小延迟会更大:
function debouncedSubscribe(store, callback) {
let timeout;
return store.subscribe(value => {
clearTimeout(timeout);
timeout = setTimeout(() => callback(value));
});
}https://stackoverflow.com/questions/72805846
复制相似问题