#typescript #svelte-3 #immer.js #svelte-store
#typescript #svelte-3 #immer.js #svelte-store
Вопрос:
Я вхожу в Svelte и ImmerJS.
Immersjs и Svelte хранилища должны иметь возможность создавать композиции очень элегантным способом.
Я попытался создать производное хранилище с возможностью записи из родительского хранилища, просто используя выражение селектора, где выражение селектора (термин, заимствованный из .NET) представляет собой лямбда-функцию, которая описывает, как получить доступ к некоторой части дерева объектов.
root => root.a.b[10].foo
Обычно это нужно было бы проанализировать, но я думаю, что immersjs уже проделал тяжелую работу с прокси и 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) amp; ((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
Ответ №1:
Для достижения этой цели используйте новый пакет под названием immer-loves-svelte, который я написал. Экспортируется единственная вызываемая функция subStore
, которая позволяет создавать дочернее хранилище из родительского
import {writable,Writable} from "svelte/store"
import {produce,Draft, isDraft} from "immer"
import {subStore} from "immer-loves-svelte"
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)
// magic happens at this line with subStore call
let foo1_a_Store:Writable<number> =
subStore(barStore, b => b.foo1.a)
barStore.subscribe(v=>console.log(v))
foo1_a_Store.set(77