🍃このブログは移転しました。
3秒後、自動的に移動します・・・。

PreactにSignalsがきた

Signals – Preact Guide

端的にいってしまうと、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`だけアップデートされるコンポーネントに置き換えてくれる。

https://github.com/preactjs/signals/blob/d25e8bac09c94ed3bad7f12fe380becd8bf1a7ad/packages/preact/src/index.ts#L140-L163

API

  • `signal(val)`
  • `computed(fn)`
  • `effect(fn)`
  • `batch(fn)`

という基本的なものが揃ってて、`untrack()`的な挙動は、`signal.value`ではなく`signal.peek()`でアクセスすることで達成可能。

Preactでコンポーネントローカルな状態にしたい場合は、`useSignal()`と`useComputed()`っていうのもある。(中身は`useMemo(signal(val))`でしかないけど)

ちなみに、コアなプリミティブたちはPreactの外でも使えるようになってて、MobXを生で使ってた身としては地味に嬉しいポイント。

https://github.com/preactjs/signals

ってかこのリポジトリにあるのがコードのすべてなのに、行数少なくてびっくりする。毎度のことながら洗練されてるし学びの塊である・・。

内部的に`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と一緒にも使えるようになってるし、なかなか断ずるのが微妙なところ。

https://github.com/preactjs/signals#react-integration

こういうUIフレームワークって、やっぱりいろいろ最適化しようとすると、全部入りにならざるを得ないのだなあ。