Solidjsで作るdependency arrayがない世界
はじめに
私は、Reactを使用してWebアプリを作っているフロントエンドエンジニアです。
先日この動画を見て、「え、Solidjsすごくね?」と思いました。
なぜなら、
- コンポーネントは一度しかレンダリングしない(関数は一回しか発火しない)
- dependency array(useMemo, useEffectとかの第二引数のアレ)がいらない
ためです。
Reactを使用してる方であれば、不要なレンダリングを抑えるためにごにょごにょしたり、dependency array周りでハマったりしたことが一度はあるのではないでしょうか。
それがSolidjsを使用するとなくなるんです。すごくないですか??
動画を見ただけだと半信半疑だったので、本当かどうかTODOアプリを作って検証してみたので、その結果を記事にしてみようと思います。
対象読者
- Solidjsに興味がある人
- 無駄なレンダリングを抑制するのに疲れた人
- dependency array地獄から抜け出したい人
動作環境
本記事で出てくるコードの動作環境は下記のとおりです。
完成品
機能としては下記を搭載してます。
- 新規登録
- 一覧表示
- ステータスの変更
- ステータスによるフィルター
本記事で紹介するコードはすべて下記リポジトリにおいてあります。
Stateの定義
Reactのstateに当たるものをSolidjsでSignalと呼ばれています。
宣言の仕方はご覧の通りほぼ同じです。
1点注意したいのが、createSignal
で返却されるのは、getter
とsetter
でどちらも関数です。したがってSignal
にアクセスする際はtodo()
といったように値を取得します。
React
const [todo, setTodo] = useState<Todo>(initialTodo);
const [todos, setTodos] = useState<Todo[]>([]);
const [filter, setFilter] = useState<Filter>("ALL");
Solidjs
const [todo, setTodo] = createSignal<Todo>(initialTodo);
const [todos, setTodos] = createSignal<Todo[]>([]);
const [filter, setFilter] = createSignal<Filter>("ALL");
変数のメモ化
変数をメモ化するAPIも用意されています。ReactでいうところのuseMemo
はcreateMemo
というものです。
こちらも定義の仕方はほぼ同じですが、第2引数に注目です。
冒頭でお伝えしたとおり、Solidjsにはdependency arrayが不要です。
この例の場合だと、createSignals
で定義したtodos
に変更があればfilteredTodos
が再生成されます。
React
const filteredTodos = useMemo(() => {
switch (filter) {
case "ALL":
return todos;
case "NEW":
return todos.filter((todo) => todo.status === "NEW");
case "DONE":
return todos.filter((todo) => todo.status === "DONE");
}
}, [filter, todos]);
Solidjs
const filteredTodos = createMemo(() => {
switch (filter()) {
case "ALL":
return todos();
case "NEW":
return todos().filter((todo) => todo.status === "NEW");
case "DONE":
return todos().filter((todo) => todo.status === "DONE");
}
});
関数のメモ化
関数のメモ化に関してはSolidjsではAPIはなく、普段どおり関数を宣言するだけで良いみたいです。
React
const handleSubmit = useCallback(
(e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const generateId = () => Math.random().toString(32).substring(2);
setTodos((prev) => [...prev, { ...todo, id: generateId() }]);
setTodo(initialTodo);
},
[todo]
);
Solidjs
const handleSubmit = (e: SubmitEvent) => {
e.preventDefault();
const generateId = () => Math.random().toString(32).substring(2);
setTodos((prev) => [...prev, { ...todo(), id: generateId() }]);
setTodo(initialTodo);
};
挙動の違い
ルートのAppコンポーネントにコンソールログを仕込んで、inputを入力したものを比べみました。Reactは入力のたびにログが発火するのに対し、Solidjsは発火しません。
これがコンポーネントが一度しかレンダリングされない、というやつですね。
React
Solidjs
おわりに
いかがでしたでしょうか。
まだ触り始めたばかりでAPIの細かい仕様を把握できてませんが、Reactに近い書き味で実装できました。
実際のプロダクトではコンポーネントを分割したり、外部のAPIからデータを取得してそれを表示したりしますが、そこら辺はまだ試せてないので、引き続き触ってみたいと思います。
Discussion