T-CREATOR

ReactのJSX をそのまま使える!SolidJS の独自構文を体感しよう

ReactのJSX をそのまま使える!SolidJS の独自構文を体感しよう

React 開発者の皆さん、新しいフロントエンドフレームワークに挑戦する際、「また新しい記法を覚えなければならないのか...」と感じることはありませんか?

SolidJS は、そんな心配を吹き飛ばしてくれる革新的なフレームワークです。なぜなら、慣れ親しんだ JSX をそのまま使いながら、独自の構文でより効率的な開発が可能だからです。

本記事では、React 開発者の視点から SolidJS の独自構文を体感していただき、「なるほど、こういう書き方の方が理にかなっているな」と感じていただけるよう、実際のコード例とエラーケースを交えて解説いたします。

JSX の共通点と相違点

共通点:馴染みのある記法

SolidJS の最大の魅力は、React の JSX とほぼ同じ記法を使える点です。

tsx// React でも SolidJS でも同じように書ける
function Welcome(props) {
  return <h1>Hello, {props.name}!</h1>;
}

基本的なコンポーネントの記法、属性の指定方法、イベントハンドラーの書き方など、多くの部分で React の知識をそのまま活用できます。

相違点:よりパフォーマンスに配慮した設計

一方で、SolidJS には独自の構文が存在し、これらがパフォーマンス向上の鍵となっています。

項目ReactSolidJS理由
条件分岐{condition && <Component ​/​>}<Show when={condition}>細かい制御とパフォーマンス最適化
ループ処理{items.map(item => <Item ​/​>)}<For each={items}>キーの自動管理と効率的な更新
状態管理useStatecreateSignalリアクティブシステムとの連携

条件分岐構文の違いを体感

React の条件分岐

React では論理演算子や if 文を使って条件分岐を行います:

tsx// React の一般的な条件分岐
function UserStatus({ isLoggedIn, user }) {
  return (
    <div>
      {isLoggedIn && <p>Welcome, {user.name}!</p>}
      {!isLoggedIn && <p>Please log in</p>}
    </div>
  );
}

SolidJS の Show コンポーネント

SolidJS では<Show>コンポーネントを使用します:

tsx// SolidJS の条件分岐
import { Show } from 'solid-js';

function UserStatus(props) {
  return (
    <div>
      <Show
        when={props.isLoggedIn}
        fallback={<p>Please log in</p>}
      >
        <p>Welcome, {props.user.name}!</p>
      </Show>
    </div>
  );
}

よくあるエラーと対処法

React から移行する際によく遭遇するエラーです:

bash# エラー: TypeError: Cannot read properties of undefined (reading 'name')
# 原因: 条件分岐が正しく動作していない
tsx// ❌ 間違った書き方(Reactの記法をそのまま使用)
function UserStatus(props) {
  return (
    <div>
      {props.isLoggedIn && (
        <p>Welcome, {props.user.name}!</p>
      )}
    </div>
  );
}

// ✅ 正しい書き方(SolidJSの記法)
function UserStatus(props) {
  return (
    <div>
      <Show when={props.isLoggedIn && props.user}>
        <p>Welcome, {props.user.name}!</p>
      </Show>
    </div>
  );
}

Show コンポーネントの高度な使い方

tsx// 複雑な条件分岐も elegantに
function StatusIndicator(props) {
  return (
    <Show
      when={props.status === 'loading'}
      fallback={
        <Show
          when={props.status === 'error'}
          fallback={<Success />}
        >
          <Error message={props.errorMessage} />
        </Show>
      }
    >
      <Loading />
    </Show>
  );
}

ループ処理構文の違いを体感

React のループ処理

React ではmapメソッドを使用してリストをレンダリングします:

tsx// React のループ処理
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>
          <span>{todo.text}</span>
          <button onClick={() => deleteTodo(todo.id)}>
            Delete
          </button>
        </li>
      ))}
    </ul>
  );
}

SolidJS の For コンポーネント

SolidJS では<For>コンポーネントを使用します:

tsx// SolidJS のループ処理
import { For } from 'solid-js';

function TodoList(props) {
  return (
    <ul>
      <For each={props.todos}>
        {(todo) => (
          <li>
            <span>{todo.text}</span>
            <button onClick={() => props.onDelete(todo.id)}>
              Delete
            </button>
          </li>
        )}
      </For>
    </ul>
  );
}

For コンポーネントの利点

  1. キーの自動管理: 手動で key を指定する必要がありません
  2. 効率的な更新: 変更された要素のみを更新
  3. より直感的: map の記法より読みやすい

よくあるエラーと対処法

bash# エラー: TypeError: Cannot read properties of undefined (reading 'map')
# 原因: For コンポーネントに配列以外を渡している
tsx// ❌ 間違った書き方
function ItemList(props) {
  return (
    <For each={props.items}>
      {(item) => <div>{item.name}</div>}
    </For>
  );
}

// ✅ 正しい書き方(デフォルト値を設定)
function ItemList(props) {
  return (
    <For each={props.items || []}>
      {(item) => <div>{item.name}</div>}
    </For>
  );
}

インデックスを使用したループ

tsx// インデックスが必要な場合
function NumberedList(props) {
  return (
    <For each={props.items}>
      {(item, index) => (
        <div>
          {index() + 1}. {item.name}
        </div>
      )}
    </For>
  );
}

状態管理構文の違いを体感

React の useState

tsx// React の状態管理
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
}

SolidJS の createSignal

tsx// SolidJS の状態管理
import { createSignal } from 'solid-js';

function Counter() {
  const [count, setCount] = createSignal(0);

  return (
    <div>
      <p>Count: {count()}</p>
      <button onClick={() => setCount(count() + 1)}>
        Increment
      </button>
    </div>
  );
}

JSX 内での扱い方の違い

最も重要な違いは、SolidJS ではシグナルを関数として呼び出す必要があることです:

tsx// React: 変数をそのまま使用
<p>Count: {count}</p>

// SolidJS: 関数として呼び出し
<p>Count: {count()}</p>

よくあるエラーと対処法

bash# エラー: Objects are not valid as a React child
# 原因: シグナルを関数として呼び出していない
tsx// ❌ 間違った書き方
function DisplayValue(props) {
  const [value, setValue] = createSignal(
    props.initialValue
  );

  return <div>{value}</div>; // エラー: シグナルオブジェクトを直接表示
}

// ✅ 正しい書き方
function DisplayValue(props) {
  const [value, setValue] = createSignal(
    props.initialValue
  );

  return <div>{value()}</div>; // 関数として呼び出し
}

複雑な状態管理

tsx// オブジェクトの状態管理
function UserProfile() {
  const [user, setUser] = createSignal({
    name: '',
    email: '',
    age: 0,
  });

  const updateName = (newName) => {
    setUser((prev) => ({ ...prev, name: newName }));
  };

  return (
    <div>
      <p>Name: {user().name}</p>
      <input
        value={user().name}
        onInput={(e) => updateName(e.target.value)}
      />
    </div>
  );
}

イベントハンドリングの違いを体感

命名規則とバインディング方法

React と SolidJS では、イベントハンドラーの命名規則が異なります:

tsx// React のイベントハンドリング
function Button({ onClick, children }) {
  return <button onClick={onClick}>{children}</button>;
}

// SolidJS のイベントハンドリング(同じ記法も可能)
function Button(props) {
  return (
    <button onClick={props.onClick}>
      {props.children}
    </button>
  );
}

SolidJS 特有のイベント構文

SolidJS では、より効率的なイベントハンドリングのための独自構文があります:

tsx// on:click による直接バインディング
function InteractiveButton(props) {
  const handleClick = () => {
    console.log('Button clicked!');
  };

  return <button on:click={handleClick}>Click me</button>;
}

// イベント修飾子の使用
function Form() {
  const handleSubmit = (e) => {
    console.log('Form submitted');
  };

  return (
    <form on:submit={handleSubmit}>
      <input type='text' />
      <button type='submit'>Submit</button>
    </form>
  );
}

よくあるエラーと対処法

bash# エラー: TypeError: Cannot read properties of undefined (reading 'preventDefault')
# 原因: イベントオブジェクトの扱い方の違い
tsx// ❌ 間違った書き方
function Form() {
  const handleSubmit = (e) => {
    e.preventDefault(); // エラーが発生する可能性
    // フォーム処理
  };

  return (
    <form onSubmit={handleSubmit}>
      <button type='submit'>Submit</button>
    </form>
  );
}

// ✅ 正しい書き方
function Form() {
  const handleSubmit = (e) => {
    e.preventDefault();
    // フォーム処理
  };

  return (
    <form on:submit={handleSubmit}>
      <button type='submit'>Submit</button>
    </form>
  );
}

パフォーマンス面での構文の違い

再レンダリングが起こらない理由

React と SolidJS の最大の違いは、レンダリング方式にあります:

tsx// React: コンポーネント全体が再レンダリング
function ReactCounter() {
  const [count, setCount] = useState(0);
  console.log('Component re-rendered'); // 毎回実行される

  return (
    <div>
      <p>Count: {count}</p>
      <ExpensiveComponent /> {/* 毎回再レンダリング */}
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
}

// SolidJS: 必要な部分のみ更新
function SolidCounter() {
  const [count, setCount] = createSignal(0);
  console.log('Component initialized'); // 初回のみ実行

  return (
    <div>
      <p>Count: {count()}</p> {/* この部分のみ更新 */}
      <ExpensiveComponent /> {/* 再レンダリングされない */}
      <button onClick={() => setCount(count() + 1)}>
        Increment
      </button>
    </div>
  );
}

リアクティブな計算

tsx// SolidJS の createMemo を使用した効率的な計算
import { createSignal, createMemo } from 'solid-js';

function ExpensiveCalculation() {
  const [input, setInput] = createSignal('');

  // 入力が変更された時のみ計算が実行される
  const expensiveResult = createMemo(() => {
    console.log('Calculating...'); // 必要な時のみ実行
    return input().split('').reverse().join('');
  });

  return (
    <div>
      <input
        value={input()}
        onInput={(e) => setInput(e.target.value)}
      />
      <p>Result: {expensiveResult()}</p>
    </div>
  );
}

移行時の注意点と独自構文のメリット

移行時によくある問題

bash# エラー: ReferenceError: React is not defined
# 原因: React のインポートをそのまま残している
tsx// ❌ 間違った書き方(React のインポートが残っている)
import React from 'react';
import { createSignal } from 'solid-js';

function Component() {
  const [count, setCount] = createSignal(0);
  return <div>{count()}</div>;
}

// ✅ 正しい書き方
import { createSignal } from 'solid-js';

function Component() {
  const [count, setCount] = createSignal(0);
  return <div>{count()}</div>;
}

プロジェクトセットアップ

SolidJS プロジェクトのセットアップは簡単です:

bash# 新しいSolidJSプロジェクトを作成
yarn create solid my-solid-app

# 既存のプロジェクトにSolidJSを追加
yarn add solid-js
yarn add -D vite-plugin-solid

段階的移行のアプローチ

大規模な React プロジェクトを移行する場合のベストプラクティス:

  1. 新しいコンポーネントから SolidJS で作成
  2. 小さなコンポーネントから段階的に移行
  3. 共通のユーティリティから移行開始
tsx// 移行例:React から SolidJS へ
// Before (React)
function UserCard({ user, onEdit }) {
  const [isEditing, setIsEditing] = useState(false);

  return (
    <div className='user-card'>
      {isEditing ? (
        <EditForm
          user={user}
          onSave={() => setIsEditing(false)}
        />
      ) : (
        <div>
          <h3>{user.name}</h3>
          <button onClick={() => setIsEditing(true)}>
            Edit
          </button>
        </div>
      )}
    </div>
  );
}

// After (SolidJS)
function UserCard(props) {
  const [isEditing, setIsEditing] = createSignal(false);

  return (
    <div class='user-card'>
      <Show
        when={isEditing()}
        fallback={
          <div>
            <h3>{props.user.name}</h3>
            <button onClick={() => setIsEditing(true)}>
              Edit
            </button>
          </div>
        }
      >
        <EditForm
          user={props.user}
          onSave={() => setIsEditing(false)}
        />
      </Show>
    </div>
  );
}

独自構文のメリット

メリット詳細具体例
パフォーマンス必要な部分のみ更新大きなリストでもスムーズな動作
メモリ効率仮想 DOM を使用しないメモリ使用量の削減
直感的制御フローが明確<Show>, <For> の使用
型安全性TypeScript との親和性より強固な型チェック

まとめ

SolidJS が独自構文を採用した理由は、パフォーマンスと開発者体験の両立にあります。

従来の React の良さを保ちながら、以下の点で大幅な改善を実現しています:

技術的な優位性

  • 再レンダリングの排除: 仮想 DOM を使わず、直接 DOM 操作
  • 細かい制御: <Show>, <For> による効率的な制御フロー
  • リアクティブシステム: シグナルベースの状態管理

開発者にとってのメリット

  • 学習コストの低減: JSX の知識をそのまま活用
  • 直感的な記法: より自然な書き方
  • パフォーマンスの向上: 特別な最適化なしで高速動作

React 開発者の皆さんにとって、SolidJS は「新しい技術を学ぶ」というよりも「より良い書き方を知る」という感覚で習得できるフレームワークです。

ぜひ実際にプロジェクトで試してみて、その違いを体感してみてください。きっと「なるほど、こういう書き方の方が理にかなっているな」と感じていただけるはずです。

関連リンク