SolidJS フック相当 API 速見表:createSignal/createMemo/createEffect… 一覧

React から SolidJS への移行を検討している方、あるいは SolidJS を始めたばかりの方にとって、React のフックに相当する API を素早く把握することは非常に重要です。
SolidJS は React と似た構文を持ちながら、リアクティビティの仕組みが根本的に異なります。そのため、React の useState
や useEffect
といったフックに対応する SolidJS の API を理解することが、効率的な開発の第一歩となるでしょう。
本記事では、SolidJS の主要な Primitives(プリミティブ)を一覧表形式で整理し、それぞれの使い方を具体的なコード例とともに解説します。
SolidJS フック相当 API 速見表
以下の表は、React のフックと SolidJS の対応する API を比較したものです。
# | React フック | SolidJS API | 用途 | リアクティブ | 再実行 |
---|---|---|---|---|---|
1 | useState | createSignal | 状態管理 | ○ | × |
2 | useMemo | createMemo | 派生値のメモ化 | ○ | ○ |
3 | useEffect | createEffect | 副作用の実行 | ○ | ○ |
4 | useCallback | 関数定義 | コールバックのメモ化 | × | × |
5 | useRef | 変数宣言 | 可変参照の保持 | × | × |
6 | useContext | useContext | コンテキストの利用 | ○ | × |
7 | useReducer | createStore | 複雑な状態管理 | ○ | × |
8 | - | createResource | 非同期データ取得 | ○ | ○ |
9 | - | onMount | マウント時の処理 | × | × |
10 | - | onCleanup | クリーンアップ処理 | × | × |
背景
React と SolidJS のリアクティビティの違い
React は仮想 DOM を利用し、コンポーネント全体を再レンダリングすることで UI を更新します。一方、SolidJS は細かい粒度のリアクティビティを採用しており、変更された部分だけを効率的に更新する仕組みです。
この根本的な違いにより、同じような機能を実現する API でも、内部動作や使い方が異なる場合があります。
以下の図は、React と SolidJS のリアクティビティの違いを示しています。
mermaidflowchart TB
subgraph react["React のアプローチ"]
react_state["状態変更"] --> react_vdom["仮想 DOM 再構築"]
react_vdom --> react_diff["差分計算"]
react_diff --> react_update["実 DOM 更新"]
end
subgraph solid["SolidJS のアプローチ"]
solid_signal["Signal 変更"] --> solid_direct["依存関係を直接更新"]
solid_direct --> solid_dom["実 DOM のみ更新"]
end
React は状態変更のたびにコンポーネント関数を再実行しますが、SolidJS のコンポーネント関数は初回の 1 回のみ実行され、以降はリアクティブな依存関係によって必要な部分だけが更新されます。
このため、React のフックの概念をそのまま SolidJS に適用しようとすると、期待通りに動作しないことがあります。
課題
React 開発者が SolidJS で直面する課題
React から SolidJS に移行する際、多くの開発者が以下のような課題に直面します。
- 再レンダリングの概念がない: React の
useEffect
の依存配列のような考え方が通用しません。 - フック名が異なる:
useState
ではなくcreateSignal
という名前で、直感的に対応が分かりにくいです。 - メモ化の必要性が異なる:
useCallback
やuseMemo
が必須だった場面が、SolidJS では不要になることがあります。 - クリーンアップの方法:
useEffect
の return 関数ではなく、onCleanup
を使います。
以下の図は、React 開発者が SolidJS で直面する主な課題を示しています。
mermaidflowchart TD
start["React 開発者"] --> q1{"useState の<br/>代わりは?"}
q1 -->|正解| s1["createSignal"]
q1 -->|誤解| e1["useState を探す"]
start --> q2{"useEffect の<br/>代わりは?"}
q2 -->|正解| s2["createEffect"]
q2 -->|誤解| e2["依存配列を探す"]
start --> q3{"useMemo の<br/>代わりは?"}
q3 -->|正解| s3["createMemo"]
q3 -->|誤解| e3["再レンダリングを<br/>期待する"]
e1 --> confusion["混乱"]
e2 --> confusion
e3 --> confusion
s1 --> success["正しい理解"]
s2 --> success
s3 --> success
これらの課題を解決するには、React のフックと SolidJS の Primitives の対応関係を正しく理解することが不可欠です。
解決策
SolidJS の主要 API とその使い方
SolidJS では、React のフックに相当する機能を「Primitives」と呼ばれる API で提供しています。それぞれの Primitives の特徴と使い方を理解することで、スムーズに SolidJS を習得できるでしょう。
以下、主要な Primitives について詳しく解説します。
createSignal - 状態管理の基本
createSignal
は React の useState
に相当する API です。状態の読み取りと更新を別々の関数として提供します。
typescriptimport { createSignal } from 'solid-js';
typescript// createSignal の基本的な使い方
const [count, setCount] = createSignal(0);
typescript// 値の読み取り(関数として呼び出す)
console.log(count()); // 0
typescript// 値の更新
setCount(1);
setCount((c) => c + 1); // 前の値を使う場合
重要な違い: React の useState
と異なり、値を取得するには関数として呼び出す必要があります。これにより、SolidJS はどこで値が使われているかを追跡できます。
createMemo - 派生値のメモ化
createMemo
は React の useMemo
に相当しますが、依存配列を指定する必要がありません。
typescriptimport { createMemo } from 'solid-js';
typescriptconst [count, setCount] = createSignal(0);
// createMemo は依存する Signal を自動的に追跡
const doubled = createMemo(() => count() * 2);
typescript// メモ化された値を取得(これも関数として呼び出す)
console.log(doubled()); // 0
setCount(5);
console.log(doubled()); // 10
createMemo
は依存する Signal が変更されたときだけ再計算されます。依存配列を手動で管理する必要がないため、バグを減らせるでしょう。
createEffect - 副作用の実行
createEffect
は React の useEffect
に相当しますが、動作が大きく異なります。
typescriptimport { createEffect } from 'solid-js';
typescriptconst [count, setCount] = createSignal(0);
// createEffect は依存する Signal を自動追跡
createEffect(() => {
console.log('Count changed:', count());
});
typescript// count が変更されると自動的に再実行される
setCount(1); // "Count changed: 1" がログ出力される
setCount(2); // "Count changed: 2" がログ出力される
重要な違い: React の useEffect
と異なり、依存配列は不要です。関数内で使用した Signal が自動的に追跡されます。
onCleanup - クリーンアップ処理
onCleanup
は、エフェクトが再実行される前やコンポーネントが破棄される前に実行される処理を登録します。
typescriptimport { createEffect, onCleanup } from 'solid-js';
typescriptcreateEffect(() => {
const timer = setInterval(() => {
console.log('Tick');
}, 1000);
// クリーンアップ処理を登録
onCleanup(() => {
clearInterval(timer);
console.log('Timer cleared');
});
});
React の useEffect
の return 関数と同じ役割ですが、より明示的に意図を表現できます。
createResource - 非同期データ取得
createResource
は、非同期データ取得を簡単に扱うための SolidJS 独自の API です。React には直接対応するフックがありません。
typescriptimport { createResource } from 'solid-js';
typescript// データ取得関数を定義
const fetchUser = async (id: number) => {
const response = await fetch(
`https://api.example.com/users/${id}`
);
return response.json();
};
typescriptconst [userId, setUserId] = createSignal(1);
// createResource でデータ取得を管理
const [user] = createResource(userId, fetchUser);
typescript// リソースの状態を確認
console.log(user.loading); // データ取得中かどうか
console.log(user.error); // エラー情報
console.log(user()); // 取得したデータ
createResource
は、ローディング状態やエラー処理を自動的に管理してくれるため、非同期処理を簡潔に記述できます。
onMount - マウント時の処理
onMount
は、コンポーネントが DOM にマウントされた直後に 1 回だけ実行される処理を登録します。
typescriptimport { onMount } from 'solid-js';
typescriptonMount(() => {
console.log('Component mounted');
// DOM 操作や初期化処理を実行
});
React の useEffect(() => {}, [])
に相当しますが、より意図が明確になります。
createStore - 複雑な状態管理
createStore
は、ネストしたオブジェクトや配列などの複雑な状態を管理するための API です。
typescriptimport { createStore } from 'solid-js/store';
typescript// ストアの作成
const [store, setStore] = createStore({
user: {
name: 'Alice',
age: 30,
},
todos: [],
});
typescript// ネストしたプロパティの更新
setStore('user', 'name', 'Bob');
setStore('user', { age: 31 }); // 部分的な更新
typescript// 配列の操作
setStore('todos', (todos) => [
...todos,
{ id: 1, text: 'New task' },
]);
createStore
は、React の useReducer
や状態管理ライブラリに近い役割を果たします。細かい粒度でリアクティビティを保持できるため、パフォーマンスに優れています。
以下の図は、SolidJS の主要 API の関係性を示しています。
mermaidflowchart LR
signal["createSignal<br/>(状態)"] --> memo["createMemo<br/>(派生値)"]
signal --> effect["createEffect<br/>(副作用)"]
memo --> effect
signal --> resource["createResource<br/>(非同期)"]
effect --> cleanup["onCleanup<br/>(クリーンアップ)"]
store["createStore<br/>(複雑な状態)"] --> memo
store --> effect
各 API は互いに連携して動作し、効率的なリアクティブシステムを構築します。
具体例
カウンターアプリで比較する React と SolidJS
React と SolidJS で同じカウンターアプリを実装し、コードの違いを確認してみましょう。
React 版のカウンター
typescriptimport React, { useState, useEffect } from 'react';
typescriptfunction Counter() {
const [count, setCount] = useState(0);
const [doubled, setDoubled] = useState(0);
// 派生値の計算
useEffect(() => {
setDoubled(count * 2);
}, [count]);
// ログ出力
useEffect(() => {
console.log('Count:', count);
}, [count]);
return (
<div>
<p>Count: {count}</p>
<p>Doubled: {doubled}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
React では、派生値を計算するために useEffect
を使い、依存配列を指定する必要があります。
SolidJS 版のカウンター
typescriptimport {
createSignal,
createMemo,
createEffect,
} from 'solid-js';
typescriptfunction Counter() {
const [count, setCount] = createSignal(0);
// 派生値の計算(依存配列不要)
const doubled = createMemo(() => count() * 2);
// ログ出力(依存配列不要)
createEffect(() => {
console.log('Count:', count());
});
return (
<div>
<p>Count: {count()}</p>
<p>Doubled: {doubled()}</p>
<button onClick={() => setCount((c) => c + 1)}>
Increment
</button>
</div>
);
}
SolidJS では、依存配列を指定する必要がなく、より簡潔に記述できます。また、値を取得する際は関数として呼び出す点に注意してください。
データ取得を含む実践的な例
次に、API からデータを取得して表示する実践的な例を見てみましょう。
データ取得用の API 関数
typescript// ユーザー情報を取得する非同期関数
const fetchUserData = async (userId: number) => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/users/${userId}`
);
if (!response.ok) {
throw new Error('Failed to fetch user data');
}
return response.json();
};
SolidJS でのデータ取得と表示
typescriptimport {
createSignal,
createResource,
Show,
} from 'solid-js';
typescriptfunction UserProfile() {
const [userId, setUserId] = createSignal(1);
// createResource でデータ取得を管理
const [user] = createResource(userId, fetchUserData);
return (
<div>
<h2>User Profile</h2>
<div>
<button onClick={() => setUserId((id) => id - 1)}>
Previous User
</button>
<button onClick={() => setUserId((id) => id + 1)}>
Next User
</button>
</div>
{/* ローディング状態の表示 */}
<Show
when={!user.loading}
fallback={<p>Loading...</p>}
>
{/* エラー処理 */}
<Show
when={!user.error}
fallback={<p>Error: {user.error}</p>}
>
<div>
<p>Name: {user()?.name}</p>
<p>Email: {user()?.email}</p>
<p>City: {user()?.address?.city}</p>
</div>
</Show>
</Show>
</div>
);
}
createResource
を使うことで、ローディング状態やエラー処理を簡潔に記述できます。userId
が変更されると自動的に再取得が行われるのもポイントです。
タイマーとクリーンアップの例
副作用とクリーンアップを含む例として、タイマーアプリを実装してみましょう。
タイマーコンポーネント
typescriptimport {
createSignal,
createEffect,
onCleanup,
onMount,
} from 'solid-js';
typescriptfunction Timer() {
const [seconds, setSeconds] = createSignal(0);
const [isRunning, setIsRunning] = createSignal(false);
// タイマーのエフェクト
createEffect(() => {
if (isRunning()) {
const interval = setInterval(() => {
setSeconds((s) => s + 1);
}, 1000);
// クリーンアップ処理
onCleanup(() => {
clearInterval(interval);
});
}
});
return (
<div>
<h2>Timer: {seconds()}s</h2>
<button onClick={() => setIsRunning(!isRunning())}>
{isRunning() ? 'Stop' : 'Start'}
</button>
<button onClick={() => setSeconds(0)}>Reset</button>
</div>
);
}
onCleanup
を使うことで、タイマーのクリーンアップ処理を適切に管理できます。isRunning
の値が変わるたびにエフェクトが再実行され、古いタイマーは自動的にクリアされます。
createStore を使った Todo アプリ
複雑な状態管理が必要な Todo アプリを createStore
で実装してみましょう。
Todo の型定義
typescripttype Todo = {
id: number;
text: string;
completed: boolean;
};
Todo アプリのストア
typescriptimport { createStore } from 'solid-js/store';
import { createSignal, For } from 'solid-js';
typescriptfunction TodoApp() {
const [store, setStore] = createStore<{ todos: Todo[] }>({
todos: [],
});
const [input, setInput] = createSignal('');
let nextId = 1;
// Todo を追加
const addTodo = () => {
const text = input().trim();
if (!text) return;
setStore('todos', (todos) => [
...todos,
{ id: nextId++, text, completed: false },
]);
setInput('');
};
// Todo の完了状態を切り替え
const toggleTodo = (id: number) => {
setStore(
'todos',
(todo) => todo.id === id,
'completed',
(completed) => !completed
);
};
// Todo を削除
const removeTodo = (id: number) => {
setStore('todos', (todos) =>
todos.filter((t) => t.id !== id)
);
};
return (
<div>
<h2>Todo List</h2>
<div>
<input
value={input()}
onInput={(e) => setInput(e.currentTarget.value)}
placeholder='Enter a task'
/>
<button onClick={addTodo}>Add</button>
</div>
<ul>
<For each={store.todos}>
{(todo) => (
<li>
<input
type='checkbox'
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span
style={{
'text-decoration': todo.completed
? 'line-through'
: 'none',
}}
>
{todo.text}
</span>
<button onClick={() => removeTodo(todo.id)}>
Delete
</button>
</li>
)}
</For>
</ul>
</div>
);
}
createStore
を使うことで、配列やオブジェクトの深い階層の変更も効率的に追跡できます。特定の Todo の completed
フィールドだけを更新しても、他の Todo には影響を与えません。
以下の図は、Todo アプリでの状態更新フローを示しています。
mermaidflowchart TD
user_input["ユーザー入力"] --> add["addTodo 実行"]
add --> update_store["setStore で<br/>todos 配列に追加"]
update_store --> reactive["リアクティブ<br/>更新"]
reactive --> ui["UI が自動更新"]
user_toggle["チェックボックス<br/>クリック"] --> toggle["toggleTodo 実行"]
toggle --> update_completed["setStore で<br/>completed を更新"]
update_completed --> reactive
user_delete["削除ボタン<br/>クリック"] --> remove["removeTodo 実行"]
remove --> filter_todos["setStore で<br/>todos をフィルタ"]
filter_todos --> reactive
ユーザーのアクションに応じて、適切な関数が実行され、ストアが更新されることで UI が自動的に反映されます。
API の組み合わせパターン
実際のアプリケーションでは、複数の Primitives を組み合わせて使います。
typescriptimport {
createSignal,
createMemo,
createEffect,
createResource,
} from 'solid-js';
typescriptfunction DataDashboard() {
// フィルター条件
const [searchQuery, setSearchQuery] = createSignal('');
const [sortOrder, setSortOrder] = createSignal<
'asc' | 'desc'
>('asc');
// データ取得
const [data] = createResource(() => fetchData());
// フィルタリングとソート
const filteredData = createMemo(() => {
const query = searchQuery().toLowerCase();
const items = data() || [];
return items
.filter((item) =>
item.name.toLowerCase().includes(query)
)
.sort((a, b) => {
const order = sortOrder() === 'asc' ? 1 : -1;
return a.name.localeCompare(b.name) * order;
});
});
// データが変更されたらログ出力
createEffect(() => {
console.log(
'Filtered data count:',
filteredData().length
);
});
return <div>{/* UI コンポーネント */}</div>;
}
このように、createResource
でデータを取得し、createSignal
でフィルター条件を管理し、createMemo
で派生値を計算し、createEffect
で副作用を実行するという組み合わせが一般的です。
まとめ
SolidJS のフック相当 API(Primitives)は、React のフックと似た機能を提供しながら、より効率的なリアクティビティシステムを実現しています。
主なポイントを振り返りましょう。
状態管理の基本: createSignal
は useState
に相当しますが、値を取得する際は関数として呼び出す必要があります。これにより、SolidJS は依存関係を自動的に追跡できます。
派生値のメモ化: createMemo
は useMemo
に相当しますが、依存配列を指定する必要がありません。使用した Signal が自動的に追跡されるため、バグを減らせるでしょう。
副作用の実行: createEffect
は useEffect
に相当しますが、こちらも依存配列は不要です。関数内で参照した Signal が変更されると自動的に再実行されます。
非同期データ取得: createResource
は SolidJS 独自の API で、非同期データ取得を簡単に扱えます。ローディング状態やエラー処理が自動的に管理されるため、コードが簡潔になります。
複雑な状態管理: createStore
を使えば、ネストしたオブジェクトや配列などの複雑な状態を効率的に管理できます。細かい粒度でリアクティビティを保持できるため、パフォーマンスに優れています。
React から SolidJS への移行は、フックから Primitives への対応関係を理解することから始まります。本記事で紹介した速見表と具体例を参考に、SolidJS の効率的なリアクティビティシステムを活用してください。
関連リンク
- article
SolidJS フック相当 API 速見表:createSignal/createMemo/createEffect… 一覧
- article
SolidJS を macOS + yarn で最速構築:ESLint・Prettier・TSconfig の鉄板レシピ
- article
SolidJS × TanStack Query vs createResource:データ取得手段の実測比較
- article
SolidJS の hydration mismatch を根絶する:原因パターン 12 と再発防止チェック
- article
SolidJS のリアクティブ思考法:signal と effect を“脳内デバッグ”で理解
- article
SolidJS で認証機能を実装する:JWT・OAuth 入門
- article
Cursor プロンプト定番 30:仕様化・分割統治・根拠提示・差分出力の句型集
- article
Cline プロンプト設計チートシート:役割・制約・出力フォーマットの定石
- article
Claude4.5 vs GPT-5 比較:日本語精度・コーディング・コストを実測評価
- article
Ansible モジュール 100 連発チートシート:file/user/service/git ほか
- article
Storybook Args/ArgTypes 速見表:Controls/Docs/Autodocs を一気に整える
- article
SolidJS フック相当 API 速見表:createSignal/createMemo/createEffect… 一覧
- blog
iPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
- blog
Googleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
- blog
【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
- blog
Googleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
- blog
Pixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
- blog
フロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来