SolidJSでstore内のobjectを空にするときはreconcileを使う
結論
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", {})
では空オブジェクトがマージされるだけであるため何も起きません。
あるkey, valueを明示的に削除するにはsetStore("path", "to", "delete", undefined)
のようにundefined
を指定する必要があります。
つまりobjectを空にしたい場合、更新前のobjectにあるkeyすべてについてsetStore(key, undefined)
をする必要があります。これはreconcile()
utilityを使うことで簡単に書くことが可能です。
reconcile()
はsetData("key", reconcile(newData))
のようにsetStore()
内で使用することが想定された関数で、storeの更新前後の値を比較して差分を算出し、差分のあるkey, valueのみを更新してくれるユーティリティです。
今回の例では、更新前に存在したkey(key.poyo
, key.puyo
, key.puni
)がnewData
に存在しないことを算出し、setState("key", "poyo", undefined)
等を自動的に実行してくれます。
これによりstore内のオブジェクトを空オブジェクトに更新することが可能です。めでたしめでたし。
Discussion