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