🚀

Signalって何?jotai-signalからのjotai-uncontrolled、これすごいです

2022/09/20に公開

導入

Reactive ProgrammingではSignalという概念があるらしいですが(実はもっと古くからある概念)、SolidJSが導入してReact Hooksとの差が明確になったのかと思います。

https://www.solidjs.com/tutorial/introduction_signals

「Signal」は Solid のリアクティビティの基礎となるものです。これらには、時間とともに変化する値が含まれており、Signal の値を変更すると、それを使用しているすべてのものが自動的に更新されます。

とのことです。ObservableとかStreamとかも似たようなものですかね。

SolidJSのポイントはSignalを使ってfine-grained reactivityを実現しているところだと思います。ReactのようにVirtual DOM(とはもう呼ばないらしい、DOMじゃないので。でも、以下VDOM)を使わずに局所的にDOMを直接更新するのでパフォーマンスが良いとのことです。Svelteが先駆けでしょうか。

Preact Signals

さらに、最近リリースされたのが、Preact Signalsです。

https://preactjs.com/blog/introducing-signals/

Preact Signalsも書き方にはよりますが、VDOMをスキップして直接更新することができるそうです。書き方も簡単です。

const count = signal(0);

const Component = () => (
  <p>Value: {count}</p>
);

パフォーマンスに注目が集まっているようですが、この書き心地も注目に値すると思います。

ところで、Preact SignalsをReactでも使うことができようです。

https://github.com/preactjs/signals/blob/main/packages/react/README.md#react-integration

ただ、React版の場合はVDOMスキップはしませんref。 しかし、render phaseのスキップはします。少し話がややこしくなりますが、Reactの広義のレンダリングは、render phaseとcommit phaseに分けられます。前者は狭義のレンダリングでVDOMを作るところまで、後者がVDOMをDOMに適用するものです。一般的にReactでは前者のrender phaseの方が処理が重く、最適化の余地が多いそうです。よって、React版のPreact Signalsにも十分価値はありそうです。

ところで、@preactjs/signals-reactのコードを覗いたところ、

https://github.com/preactjs/signals/blob/2ab715770450dd5238a77ff76f4e0031d8452026/packages/react/src/index.ts#L7

となってました。社員じゃないのでクビにはなりませんが、以前internalsを使ってしばらくしたら使えなくなる経験をしたので、このアプローチは避けたいです。

jotai-signal

ふと、jsx-transformをいじれば同じようなことができるのではないかと思って、試したところうまくいきました。jotai-signalの誕生です。

https://twitter.com/dai_shi/status/1569542040010260482

使い方は同じ感じです。jotaiではatomが既に状態の定義として存在するので、それをsignal()で囲ってSignal化します。(ちなみに、通常のsignalはsignal自体に値が含まれるかもしれませんが、jotai-signalの場合はatom自体には値は含まれません。)

https://github.com/jotaijs/jotai-signal/blob/e9b22914f1b6e2643f42a4591b83c793eb3c9fd9/examples/01_typescript/src/App.tsx#L1-L15

一行目の@jsxImportSourceがポイントです。ソースコード中に書く代わりに、tsconfig.jsonに書くこともできます。このアプローチも一種のハックではありますが、internalsよりはだいぶマシではないかと思います。

propsにも使えます。

https://twitter.com/dai_shi/status/1569648820551823360

jotai-uncontrolled

jotai-signalで@preactjs/signals-reactと同等のことはできたと思いますが、本家@preactjs/signalsのようにVDOMのスキップはできません。

なんとかならないかと考えていたところ、react-hook-formのregister()を思い出しました。つまり、uncontrolled componentにすればVDOMのスキップができそうです。jotai-uncontrolledの誕生です。

https://twitter.com/dai_shi/status/1570361010757959682

v0.1.0での使い方は、ref={}を指定するものです。

https://github.com/jotaijs/jotai-uncontrolled/blob/8fd013da5b6f398ea7b67279c0acf218fb3a0d14/examples/01_typescript/src/App.tsx#L3-L15

特殊な書き方ではありますが、jotai-signalの場合のような@jsxImportSourceは必要ありません。

すると、ある人が新しいAPIを提案してくれました。

https://twitter.com/grumd_osu/status/1570511600200843264

この書き方だと、ref={}を明示する必要がなく自然な感じです。採用。

https://twitter.com/dai_shi/status/1570703789270646784

使い方はspanの代わりにuncontrolled.spanにするだけです。

https://github.com/jotaijs/jotai-uncontrolled/blob/5333197455f58ad1c63023f5f40a4ecdaa6f7f53/examples/01_typescript/src/App.tsx#L3-L16

jotai-signalの場合と違ってsignal()で囲う必要もありません。(あっちも頑張れば、atom自体をsignalのように扱えるかも)

ちなみに、TypeScriptの型がバッチリ当たっていて、ミスなく使えるはずです。さらに、この仕組みは実はreact-dom以外のrendererに発展させることもできそうです。軽く試した感じでは、react-three-fiberでも動きました。react-nativeに対応させることももしかしたらできるかもしれません。

比較

コードを比較するとこのようになります。

https://twitter.com/dai_shi/status/1570713352137064448

jotai-signalと比べて、jotai-uncontrolledは、書き方はシンプルになり、ハックは減り、パフォーマンスも改善する、と言う夢のようなライブラリになりました。

ただし、jotai-uncontrolledはVDOMをスキップするため、VDOMだからこそできることは一切できません。例えば、Suspenseなどです。ざっくりとした比較表がこちら。

https://twitter.com/dai_shi/status/1570719014489292802

「(DOM only)」は上記のようにもしかしたら改善するかもしれません。Immutability/Concurrencyは正確でないかもしれません。

jotai-uncontrolledは機能的には劣るように見えますが、それでもゲームのようなアプリだったり、Suspenseの必要性がなくパフォーマンス重視の場合には役立つかと思います。何より通常のjotaiライブラリと共存が可能です(これはjotai-uncontrolledだけではなくjotai-signalにも当てはまります)。必要なコンポーネントのみにuncontrolled.*を使えば良いのです。書き心地がよければパフォーマンス重視でなくても使って良いかもしれません。

jotai-signalは、jotaiとjotai-uncontrolledの中間解のような位置付けになってしまったので、逆にあまり利用シーンが思い浮かばなくなりました。jsx-transformでできることの可能性を確認するという点では価値がありますし、useMemoを必要としないため、通常のjotaiライブラリよりは書き心地は良くなりそうです。

ご意見ご感想などありましたら、ぜひ。

リンク集

https://github.com/jotaijs/jotai-signal

https://github.com/jotaijs/jotai-uncontrolled

Discussion