端的にいってしまうと、Solidのソレとほぼ同様の体験でコードが書けるようになる・・・!
まずはコード
// store.js import { signal } from "@preact/signals"; export const count = signal(0); export const add = () => count.value++; // Counter.jsx import { count, add } from "./store.js"; const Counter = () => ( <div> <p>Count: {count.value}</p> <button onClick={add}>click me</button> </div> );
さすがにこの時代になるといろいろ既視感もあって、すんなり読めるコードかと。
値は`.value`に入ってて、それにアクセスしたコンポーネントが、更新時にre-renderされる。
途中のコンポーネントでバケツリレーされてても、Signal自体はただの参照であるがゆえに、re-renderされない。グローバルに定義すれば、バケツリレーする必要すらない。
同様のことは今までもMobXなり他のライブラリでもできたけど、だいたいは`observe()`的な関数でコンポーネントをHoCにしたり、必要なものを`selector()`で切り出す必要があって、そこが地味にいろいろ不便だったはず。
それが今、3rdではなくビルトインで提供されるというところがポイント。もう迷わない!
Preactでの最適化
なんと実は`.value`でアクセスする必要すらない。
const count = signal(0); // こう書くと、コンポーネント単位でre-renderされる const Counter = () => <p>{count.value}</p>; // こう書けば、TextNodeだけre-renderできる!! const Counter = () => <p>{count}</p>;
これをシームレスに達成できるのすばらしい・・。
ちなみに、PreactにはそのVDOMレンダリングフェーズに介入できる機構(Options Hook)がもともとあって、それで拡張してる。
この例だと、`children`が`Signal`だったらば、そこを`Text`だけアップデートされるコンポーネントに置き換えてくれる。
API
- `signal(val)`
- `computed(fn)`
- `effect(fn)`
- `batch(fn)`
という基本的なものが揃ってて、`untrack()`的な挙動は、`signal.value`ではなく`signal.peek()`でアクセスすることで達成可能。
Preactでコンポーネントローカルな状態にしたい場合は、`useSignal()`と`useComputed()`っていうのもある。(中身は`useMemo(signal(val))`でしかないけど)
ちなみに、コアなプリミティブたちはPreactの外でも使えるようになってて、MobXを生で使ってた身としては地味に嬉しいポイント。
ってかこのリポジトリにあるのがコードのすべてなのに、行数少なくてびっくりする。毎度のことながら洗練されてるし学びの塊である・・。
内部的に`Proxy`は使ってなくて、`.value = xxx`っていう値の代入でしかアップデートできないので、そこはもしかしたらちょっと不便に感じるかもしれない。
あとは、Svelteのstoreみたく`subscribe()`だけ公開してReadOnlyにする仕組みとか、MobXの`action()`みたくそれ経由でしかアップデートできないようにしたいとか、そういうのはまあ出てくるんやろうなーとは思った。
まとめ
- `useState()`に代わる新しい状態管理の仕組みである`signal()`が、Preactに追加された
- 値を更新する(setterを叩く)だけで、必要な箇所だけが自動でre-renderされる
- 参照を直で使えば、レンダリングも最適化できる
同様の書き味がほしいのであれば、SolidやSvelteのそれでもよかったけど、V-DOMがほしいならVueしかない・・っていうところに、Preactっていう選択肢が増えたって感じで、今後にも注目。
Reactから離れて独自路線に舵を切ったかどうかでいうと、`useErrorBoundary()`みたいなReactに存在しないAPIも前からあったし、SignalsもReactと一緒にも使えるようになってるし、なかなか断ずるのが微妙なところ。
こういうUIフレームワークって、やっぱりいろいろ最適化しようとすると、全部入りにならざるを得ないのだなあ。