Signalって何?jotai-signalからのjotai-uncontrolled、これすごいです
導入
Reactive ProgrammingではSignalという概念があるらしいですが(実はもっと古くからある概念)、SolidJSが導入してReact Hooksとの差が明確になったのかと思います。
「Signal」は Solid のリアクティビティの基礎となるものです。これらには、時間とともに変化する値が含まれており、Signal の値を変更すると、それを使用しているすべてのものが自動的に更新されます。
とのことです。ObservableとかStreamとかも似たようなものですかね。
SolidJSのポイントはSignalを使ってfine-grained reactivityを実現しているところだと思います。ReactのようにVirtual DOM(とはもう呼ばないらしい、DOMじゃないので。でも、以下VDOM)を使わずに局所的にDOMを直接更新するのでパフォーマンスが良いとのことです。Svelteが先駆けでしょうか。
Preact Signals
さらに、最近リリースされたのが、Preact Signalsです。
Preact Signalsも書き方にはよりますが、VDOMをスキップして直接更新することができるそうです。書き方も簡単です。
const count = signal(0);
const Component = () => (
<p>Value: {count}</p>
);
パフォーマンスに注目が集まっているようですが、この書き心地も注目に値すると思います。
ところで、Preact SignalsをReactでも使うことができようです。
ただ、React版の場合はVDOMスキップはしませんref。 しかし、render phaseのスキップはします。少し話がややこしくなりますが、Reactの広義のレンダリングは、render phaseとcommit phaseに分けられます。前者は狭義のレンダリングでVDOMを作るところまで、後者がVDOMをDOMに適用するものです。一般的にReactでは前者のrender phaseの方が処理が重く、最適化の余地が多いそうです。よって、React版のPreact Signalsにも十分価値はありそうです。
ところで、@preactjs/signals-react
のコードを覗いたところ、
となってました。社員じゃないのでクビにはなりませんが、以前internalsを使ってしばらくしたら使えなくなる経験をしたので、このアプローチは避けたいです。
jotai-signal
ふと、jsx-transformをいじれば同じようなことができるのではないかと思って、試したところうまくいきました。jotai-signalの誕生です。
使い方は同じ感じです。jotaiではatomが既に状態の定義として存在するので、それをsignal()
で囲ってSignal化します。(ちなみに、通常のsignalはsignal自体に値が含まれるかもしれませんが、jotai-signalの場合はatom自体には値は含まれません。)
一行目の@jsxImportSource
がポイントです。ソースコード中に書く代わりに、tsconfig.jsonに書くこともできます。このアプローチも一種のハックではありますが、internalsよりはだいぶマシではないかと思います。
propsにも使えます。
jotai-uncontrolled
jotai-signalで@preactjs/signals-react
と同等のことはできたと思いますが、本家@preactjs/signals
のようにVDOMのスキップはできません。
なんとかならないかと考えていたところ、react-hook-formのregister()
を思い出しました。つまり、uncontrolled componentにすればVDOMのスキップができそうです。jotai-uncontrolledの誕生です。
v0.1.0での使い方は、ref={}
を指定するものです。
特殊な書き方ではありますが、jotai-signalの場合のような@jsxImportSource
は必要ありません。
すると、ある人が新しいAPIを提案してくれました。
この書き方だと、ref={}
を明示する必要がなく自然な感じです。採用。
使い方はspan
の代わりにuncontrolled.span
にするだけです。
jotai-signalの場合と違ってsignal()
で囲う必要もありません。(あっちも頑張れば、atom自体をsignalのように扱えるかも)
ちなみに、TypeScriptの型がバッチリ当たっていて、ミスなく使えるはずです。さらに、この仕組みは実はreact-dom以外のrendererに発展させることもできそうです。軽く試した感じでは、react-three-fiberでも動きました。react-nativeに対応させることももしかしたらできるかもしれません。
比較
コードを比較するとこのようになります。
jotai-signalと比べて、jotai-uncontrolledは、書き方はシンプルになり、ハックは減り、パフォーマンスも改善する、と言う夢のようなライブラリになりました。
ただし、jotai-uncontrolledはVDOMをスキップするため、VDOMだからこそできることは一切できません。例えば、Suspenseなどです。ざっくりとした比較表がこちら。
「(DOM only)」は上記のようにもしかしたら改善するかもしれません。Immutability/Concurrencyは正確でないかもしれません。
jotai-uncontrolledは機能的には劣るように見えますが、それでもゲームのようなアプリだったり、Suspenseの必要性がなくパフォーマンス重視の場合には役立つかと思います。何より通常のjotaiライブラリと共存が可能です(これはjotai-uncontrolledだけではなくjotai-signalにも当てはまります)。必要なコンポーネントのみにuncontrolled.*
を使えば良いのです。書き心地がよければパフォーマンス重視でなくても使って良いかもしれません。
jotai-signalは、jotaiとjotai-uncontrolledの中間解のような位置付けになってしまったので、逆にあまり利用シーンが思い浮かばなくなりました。jsx-transformでできることの可能性を確認するという点では価値がありますし、useMemoを必要としないため、通常のjotaiライブラリよりは書き心地は良くなりそうです。
ご意見ご感想などありましたら、ぜひ。
リンク集
Discussion