我正在研究 Svelte 和 ImmerJS。
Immerjs 和 Svelte 商店应该能够以非常优雅的方式进行组合。
我试图仅使用选择器表达式从父存储创建派生的可写存储,其中选择器表达式(从 .NET 借用的术语)是一个函数 lambda,它描述了如何访问对象树的某些子部分。
root => root.a.b[10].foo
通常必须解析这个,但我认为 immerjs 已经用代理和Draft<T>
类完成了艰苦的工作。
所以目的是能够做到以下几点。
import {writable} from "svelte/store"
import {lens} from "my_magic_lens_library_not_yet_written"
interface Foo {
a: number
b: string
}
interface Bar {
foo1: Foo
foo2: Foo
}
let bar:Bar = {
foo1: {a:10, b:"monkey"},
foo2: {a:20 , b:"cat"}
}
let barStore:Writable<Bar> = writable(bar)
let foo1_a_Store:Writable<number> = lens(barStore, (b:Draft<Bar>) => b.foo1.a)
barStore.subscribe(v=>console.log(v))
foo1_a_Store.set(77)
我希望输出是
{ foo1: { a: 10, b: 'monkey' }, foo2: { a: 20, b: 'cat' } }
{ foo1: { a: 77, b: 'monkey' }, foo2: { a: 20, b: 'cat' } }
但它是
{ foo1: { a: 10, b: 'monkey' }, foo2: { a: 20, b: 'cat' } }
{ foo1: { a: 10, b: 'monkey' }, foo2: { a: 20, b: 'cat' } }
镜头的实现是
import {writable,Writable} from "svelte/store"
import {produce,Draft} from "immer"
type Updater<T> = (arg0:T)=>T
type Selector<T,U> = ((ar:T)=>U) & ((ar:Draft<T>)=>Draft<U>);
function lens<T,U>(store:Writable<T>, selector:Selector<T,U>):Writable<U>
{
let {subscribe, set, update} = store
function subSet(v:U):void
{
let rootUpdater = (oldValue:T) => {
return produce(
oldValue,
(ds:Draft<T>) => {
let subDraft:Draft<U> = selector(ds)
Object.assign(subDraft , v)
}
)
}
update(rootUpdater)
}
function subUpdate(updater:Updater<U>):void
{
let rootUpdater = (oldValue:T) => {
return produce(
oldValue,
(ds:Draft<T>) => {
let subDraft:Draft<U> = selector(ds)
Object.assign(subDraft , updater(selector(oldValue)))
}
)
}
update(rootUpdater)
}
return {
subscribe: subscriber => subscribe(v=>subscriber(selector(v))),
set: subSet,
update: subUpdate
}
}
我很确定失败的线路是
Object.assign(subDraft , updater(selector(oldValue)))
我尝试将更新的值传播到子草稿中。谁知道这是否可能?但它应该是。有人能找出神奇的酱汁来让它发挥作用吗?
repl.it 上有一个实时版本