🫗

SolidJSでstore内のobjectを空にするときはreconcileを使う

2024/03/30に公開

結論

SolidJSにおいてstore内のオブジェクトを空オブジェクト{}にしたい場合setStore("path", "to", "obj", reconcile({}))とする必要があります。
setStore("path", "to", "obj", {})では意図した挙動になりません。

例えば以下のようにする必要があります

import { createStore, reconcile } from "solid-js/store";

const [state, setState] = createStore({
  hoge: {
    poyo: 1,
    puyo: 2,
    puni: 3
  }
});

// stateを { hoge: {} } にしたい
// これでは正しく更新されない
setState("hoge", {})

// 以下のようにすると更新される
setState("hoge", reconcile({}))

playgroundでのデモ:https://playground.solidjs.com/anonymous/0ed5f9ec-ed8d-46e7-9121-324861513e41

なおこの方法はSolid公式Discord内で言及されていたものです:https://discord.com/channels/722131463138705510/974612509573136404/974750349497876510

背景

SolidJSを使用したとあるプロジェクトにおいて、「store内のネストした位置にあるオブジェクトを空にしたい」という状況が発生しました。
(本来であれば頻繁にkey,valueを追加/削除する場合Mapを使うべきかもしれませんが、SolidJSのstoreではMapの更新にeffectが細かく反応しないためobjectを使っていました)

何も考えずにsetStore("path", "to", "obj", {})としたところ、store内のオブジェクトは空にならずeffectも反応しませんでした😢

原理

Solidのstoreにおけるオブジェクトset時の挙動は浅いmergeでありsetStore("path", {})では空オブジェクトがマージされるだけであるため何も起きません。


https://docs.solidjs.com/concepts/stores#modifying-objects

あるkey, valueを明示的に削除するにはsetStore("path", "to", "delete", undefined)のようにundefinedを指定する必要があります。
つまりobjectを空にしたい場合、更新前のobjectにあるkeyすべてについてsetStore(key, undefined)をする必要があります。これはreconcile() utilityを使うことで簡単に書くことが可能です。


https://docs.solidjs.com/concepts/stores#data-integration-with-reconcile

reconcile()setData("key", reconcile(newData))のようにsetStore()内で使用することが想定された関数で、storeの更新前後の値を比較して差分を算出し、差分のあるkey, valueのみを更新してくれるユーティリティです。
今回の例では、更新前に存在したkey(key.poyo, key.puyo, key.puni)がnewDataに存在しないことを算出し、setState("key", "poyo", undefined)等を自動的に実行してくれます。


https://github.com/solidjs/solid/blob/dbdc27df2246ec05c0e32cf7b461ddbe4223915f/packages/solid/store/src/modifiers.ts#L115-L117

これによりstore内のオブジェクトを空オブジェクトに更新することが可能です。めでたしめでたし。

Discussion