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 article- SolidJS の Control Flow コンポーネント大全:Show/For/Switch/ErrorBoundary を使い分け
 article article- SolidJS 本番運用チェックリスト:CSP・SRI・Preload・エラーレポートの総点検
 article article- SolidJS クリーンアーキテクチャ実践:UI・状態・副作用を厳密に分離する
 article article- SolidJS フック相当 API 速見表:createSignal/createMemo/createEffect… 一覧
 article article- SolidJS を macOS + yarn で最速構築:ESLint・Prettier・TSconfig の鉄板レシピ
 article article- SolidJS × TanStack Query vs createResource:データ取得手段の実測比較
 article article- MySQL ERROR 1449 対策:DEFINER 不明でビューやトリガーが壊れた時の復旧手順
 article article- Cursor で差分が崩れる/意図しない大量変更が入るときの復旧プレイブック
 article article- Motion(旧 Framer Motion)で exit が発火しない/遅延する問題の原因切り分けガイド
 article article- JavaScript 時刻の落とし穴大全:タイムゾーン/DST/うるう秒の実務対策
 article article- Cline が差分を誤適用する時:改行コード・Prettier・改フォーマット問題の解決
 article article- htmx で二重送信が起きる/起きない問題の完全対処:trigger と disable パターン
 blog blog- iPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
 blog blog- Googleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
 blog blog- 【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
 blog blog- Googleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
 blog blog- Pixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
 blog blog- フロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
 review review- 今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
 review review- ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
 review review- 愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
 review review- 週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
 review review- 新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
 review review- 科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来