ryokatsu.dev

preactのsignalsのドキュメントとソースコードを少し読んだ


これを読みました。

signalによる最適化

今までuseStateで書いていた部分をsignal()と書くことで状態管理を行えます。signalには.valueというプロパティがあり、この.valueが更新される時だけ再レンダリングが走る仕組みになっています。setState的なのは、内部的にはこのあたりでやってます

つまりpropsで渡ってきた時などでは、参照のみになるため再レンダリングが走らないようです。(useStateだと毎回再レンダリングされる)

値更新時に、現在の値を同じ値を代入してもsignal自体は更新されません。

以下ベーシックな記述を公式からそのまま拝借

import { signal } from "@preact/signals";

// Create a signal that can be subscribed to:
const count = signal(0);

function Counter() {
  // Accessing .value in a component automatically re-renders when it changes:
  const value = count.value;

  const increment = () => {
    // A signal is updated by assigning to the `.value` property:
    count.value++;
  }

  return (
    <div>
      <p>Count: {value}</p>
      <button onClick={increment}>click me</button>
    </div>
  );
}

API

状態管理系で必要そうなAPIは、以下のように揃っていそうです。

  • 他のsignalを元に計算して、新しいsignalを作るときはcomputed()を使用できます。
  • signalが変更した時に何か処理をしたいときは、effect()を使用できます。(useEffectと似ていますが若干違いそう。)
  • 複数の値の更新を1つにまとめて行う関数として、batch(() => {})が使用できます。

また、signalは、セッターを叩けば値が更新される仕様上、グローバルな状態管理(useContextみたいな使い方)として使用できます。なのでコンポーネントだけに状態を、閉じたい場合はuseSignal()のようなHookが用意されています。内部実装的には、coreに各種APIのビジネスロジックがあり、preactとreactそれぞれHooksとして提供できるように、別ファイルに切り分けられていました。

https://github.com/preactjs/signals/tree/main/packages

実は、Reactでも使用できるんですね。。。

感想

やっぱり値の更新の記述が、一番気になる感じでしょうか。useStateだとset関数を必ず使うことで、なんとなく分かりやすかったですが、signalの場合は.valueに代入する書き方になるので慣れるかどうかです。ただ今までも、オブジェクトや配列の一部の値を更新したい時は、useStateで似たような書き方はしていたので、あまり気にならないかも。

グローバルなsignalだけちょっと怖さもあるのですが、個人的には、結構良いなと思いました。

後実際のソースコードの量がかなりシンプルなので、読むだけでお勉強になりました。