React Compiler実践ガイド2025 - 自動最適化でパフォーマンス革命
React Compilerによる自動最適化の仕組みと実践的な導入方法を徹底解説。memo化の自動化、バンドルサイズ削減、実プロジェクトでの効果測定まで、2025年最新の情報をお届けします。
パフォーマンス革命をもたらすSolidJSの実践的活用法を徹底解説。React比3倍高速を実現する仕組みから実際のプロジェクト構築、移行戦略まで、具体的なコード例とベンチマークデータで詳しく紹介します。
SolidJS は 2025 年、フロントエンド開発における真のパフォーマンス革命として確固たる地位を築きました。React比 3 倍の高速化を実現しながらも、慣れ親しんだ JSX 記法を保持するこのフレームワークは、多くの開発者と企業から注目を集めています。本記事では、SolidJS の核心機能から実践的な活用法、Reactからの移行戦略まで、包括的に解説します。
SolidJS は「真のリアクティブ」を実現する革新的なフロントエンドフレームワークです。従来のバーチャル DOM による差分更新ではなく、変更された部分のみを直接更新する「細粒度リアクティビティ」を実装しています。
特徴 | SolidJS | React | 優位性 |
---|---|---|---|
更新方式 | 細粒度リアクティビティ | Virtual DOM差分 | 3-5倍高速 |
バンドルサイズ | ~7KB | ~45KB | 85%削減 |
初期描画 | 50ms未満 | 150ms未満 | 3倍高速 |
メモリ使用量 | 低 | 中-高 | 40%削減 |
学習コスト | 低(JSX慣れ) | 基準 | React知識活用可 |
エコシステム | 急成長中 | 成熟 | 必要十分 |
チャートを読み込み中...
SolidJS の核心となる Signal システムを理解しましょう:
import { createSignal, createEffect, createMemo } from 'solid-js';
// 基本的なSignal
const [count, setCount] = createSignal(0);
// 計算されたSignal(Memo)
const doubleCount = createMemo(() => count() * 2);
// 副作用(Effect)
createEffect(() => {
console.log(`現在のカウント: ${count()}, 2倍: ${doubleCount()}`);
});
// 値の更新
setCount(5); // Effect が自動実行される
// React での状態管理
function Counter() {
const [count, setCount] = useState(0);
const [multiplier, setMultiplier] = useState(2);
// 依存関係を明示的に指定
const result = useMemo(() =>
count * multiplier, [count, multiplier]
);
// 副作用の依存関係を管理
useEffect(() => {
document.title = `カウント: ${result}`;
}, [result]);
return (
<div>
<p>結果: {result}</p>
<button onClick={() => setCount(c => c + 1)}>
+1
</button>
</div>
);
}
// SolidJS での状態管理
function Counter() {
const [count, setCount] = createSignal(0);
const [multiplier, setMultiplier] = createSignal(2);
// 自動的な依存関係追跡
const result = createMemo(() =>
count() * multiplier()
);
// 依存関係は自動検出
createEffect(() => {
document.title = `カウント: ${result()}`;
});
return (
<div>
<p>結果: {result()}</p>
<button onClick={() => setCount(c => c + 1)}>
+1
</button>
</div>
);
}
// React での状態管理
function Counter() {
const [count, setCount] = useState(0);
const [multiplier, setMultiplier] = useState(2);
// 依存関係を明示的に指定
const result = useMemo(() =>
count * multiplier, [count, multiplier]
);
// 副作用の依存関係を管理
useEffect(() => {
document.title = `カウント: ${result}`;
}, [result]);
return (
<div>
<p>結果: {result}</p>
<button onClick={() => setCount(c => c + 1)}>
+1
</button>
</div>
);
}
// SolidJS での状態管理
function Counter() {
const [count, setCount] = createSignal(0);
const [multiplier, setMultiplier] = createSignal(2);
// 自動的な依存関係追跡
const result = createMemo(() =>
count() * multiplier()
);
// 依存関係は自動検出
createEffect(() => {
document.title = `カウント: ${result()}`;
});
return (
<div>
<p>結果: {result()}</p>
<button onClick={() => setCount(c => c + 1)}>
+1
</button>
</div>
);
}
// 高パフォーマンスなコンポーネント設計
import { createSignal, createMemo, For, Show } from 'solid-js';
function TodoApp() {
const [todos, setTodos] = createSignal([]);
const [filter, setFilter] = createSignal('all'); // 'all' | 'active' | 'completed'
// 効率的なフィルタリング
const filteredTodos = createMemo(() => {
const currentTodos = todos();
const currentFilter = filter();
switch (currentFilter) {
case 'active':
return currentTodos.filter(todo => !todo.completed);
case 'completed':
return currentTodos.filter(todo => todo.completed);
default:
return currentTodos;
}
});
// 統計情報の計算
const stats = createMemo(() => {
const currentTodos = todos();
return {
total: currentTodos.length,
completed: currentTodos.filter(todo => todo.completed).length,
active: currentTodos.filter(todo => !todo.completed).length
};
});
const addTodo = (text) => {
setTodos(prev => [...prev, {
id: Date.now(),
text,
completed: false,
createdAt: new Date()
}]);
};
const toggleTodo = (id) => {
setTodos(prev => prev.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
const deleteTodo = (id) => {
setTodos(prev => prev.filter(todo => todo.id !== id));
};
return (
<div class="todo-app">
<TodoInput onAdd={addTodo} />
<TodoStats stats={stats()} />
<TodoFilters
current={filter()}
onChange={setFilter}
/>
<div class="todo-list">
<For each={filteredTodos()}>
{(todo) => (
<TodoItem
todo={todo}
onToggle={() => toggleTodo(todo.id)}
onDelete={() => deleteTodo(todo.id)}
/>
)}
</For>
<Show when={filteredTodos().length === 0}>
<EmptyState filter={filter()} />
</Show>
</div>
</div>
);
}
// 最適化されたTodoItemコンポーネント
function TodoItem(props) {
// プロパティは自動的にリアクティブ
const todo = () => props.todo;
return (
<div
class="todo-item"
classList={{
completed: todo().completed,
recent: isRecent(todo().createdAt)
}}
>
<input
type="checkbox"
checked={todo().completed}
onChange={props.onToggle}
/>
<span class="todo-text">{todo().text}</span>
<button
class="delete-btn"
onClick={props.onDelete}
>
削除
</button>
</div>
);
}
function isRecent(date) {
return Date.now() - date.getTime() < 5 * 60 * 1000; // 5分以内
}
Vite + SolidJS テンプレートの設定
TypeScript、ESLint、Prettier設定
solid-router による SPA 構築
グローバル状態とローカル状態の設計
# Vite + SolidJS テンプレート
npm create solid@latest my-solid-app
# またはTypeScriptテンプレート
npm create solid@latest my-solid-app -- --template ts
cd my-solid-app
npm install
# 開発サーバー起動
npm run dev
src/
├── components/ # 再利用可能コンポーネント
│ ├── ui/ # UI基盤コンポーネント
│ ├── forms/ # フォーム関連
│ └── layout/ # レイアウトコンポーネント
├── pages/ # ページコンポーネント
├── stores/ # グローバル状態管理
├── utils/ # ユーティリティ関数
├── types/ # TypeScript型定義
├── styles/ # スタイル定義
└── App.tsx # アプリケーションルート
// App.tsx - ルーティング設定
import { Router, Route, Routes } from '@solidjs/router';
import { lazy } from 'solid-js';
// 遅延ローディング
const Home = lazy(() => import('./pages/Home'));
const Products = lazy(() => import('./pages/Products'));
const ProductDetail = lazy(() => import('./pages/ProductDetail'));
const Profile = lazy(() => import('./pages/Profile'));
function App() {
return (
<Router>
<Routes>
<Route path="/" component={Home} />
<Route path="/products" component={Products} />
<Route path="/products/:id" component={ProductDetail} />
<Route path="/profile" component={Profile} />
<Route path="*" component={() => <div>404 Not Found</div>} />
</Routes>
</Router>
);
}
export default App;
// pages/ProductDetail.tsx - 動的ルーティング
import { useParams, useNavigate } from '@solidjs/router';
import { createResource, Show, Suspense } from 'solid-js';
async function fetchProduct(id: string) {
const response = await fetch(`/api/products/${id}`);
if (!response.ok) throw new Error('Product not found');
return response.json();
}
function ProductDetail() {
const params = useParams();
const navigate = useNavigate();
// リソースによる非同期データ取得
const [product] = createResource(
() => params.id,
fetchProduct
);
return (
<div class="product-detail">
<button onClick={() => navigate('/products')}>
← 商品一覧に戻る
</button>
<Suspense fallback={<ProductSkeleton />}>
<Show
when={product()}
fallback={<ProductNotFound />}
>
{(product) => (
<div class="product-content">
<h1>{product().name}</h1>
<img src={product().image} alt={product().name} />
<p class="price">¥{product().price.toLocaleString()}</p>
<p class="description">{product().description}</p>
<ProductActions product={product()} />
</div>
)}
</Show>
</Suspense>
</div>
);
}
// stores/userStore.ts - Store パターン
import { createSignal, createMemo } from 'solid-js';
interface User {
id: string;
name: string;
email: string;
role: 'admin' | 'user';
}
// グローバル状態
const [user, setUser] = createSignal<User | null>(null);
const [isLoading, setIsLoading] = createSignal(false);
const [error, setError] = createSignal<string | null>(null);
// 計算された値
export const isAuthenticated = createMemo(() => user() !== null);
export const isAdmin = createMemo(() => user()?.role === 'admin');
// アクション
export const userActions = {
async login(email: string, password: string) {
setIsLoading(true);
setError(null);
try {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
if (!response.ok) {
throw new Error('ログインに失敗しました');
}
const userData = await response.json();
setUser(userData);
} catch (err) {
setError(err instanceof Error ? err.message : 'エラーが発生しました');
} finally {
setIsLoading(false);
}
},
logout() {
setUser(null);
localStorage.removeItem('auth-token');
},
updateProfile(updates: Partial<User>) {
setUser(prev => prev ? { ...prev, ...updates } : null);
}
};
// エクスポート
export { user, isLoading, error };
// contexts/AppContext.tsx - Context パターン
import { createContext, useContext, ParentComponent } from 'solid-js';
import { createStore } from 'solid-js/store';
interface AppState {
theme: 'light' | 'dark';
language: 'ja' | 'en';
notifications: Notification[];
}
interface AppContextType {
state: AppState;
actions: {
setTheme: (theme: AppState['theme']) => void;
setLanguage: (lang: AppState['language']) => void;
addNotification: (notification: Notification) => void;
};
}
const AppContext = createContext<AppContextType>();
export const AppProvider: ParentComponent = (props) => {
const [state, setState] = createStore<AppState>({
theme: 'light',
language: 'ja',
notifications: []
});
const actions = {
setTheme: (theme: AppState['theme']) => {
setState('theme', theme);
localStorage.setItem('theme', theme);
},
setLanguage: (language: AppState['language']) => {
setState('language', language);
localStorage.setItem('language', language);
},
addNotification: (notification: Notification) => {
setState('notifications', prev => [...prev, notification]);
// 自動削除
setTimeout(() => {
setState('notifications', prev =>
prev.filter(n => n.id !== notification.id)
);
}, 5000);
}
};
return (
<AppContext.Provider value={{ state, actions }}>
{props.children}
</AppContext.Provider>
);
};
export function useApp() {
const context = useContext(AppContext);
if (!context) {
throw new Error('useApp must be used within AppProvider');
}
return context;
}
// stores/cartStore.ts - External Store パターン
import { createStore } from 'solid-js/store';
import { createEffect } from 'solid-js';
interface CartItem {
id: string;
name: string;
price: number;
quantity: number;
image: string;
}
interface CartStore {
items: CartItem[];
total: number;
tax: number;
grandTotal: number;
}
// Store の作成
const [cartStore, setCartStore] = createStore<CartStore>({
items: [],
total: 0,
tax: 0,
grandTotal: 0
});
// 計算の自動更新
createEffect(() => {
const total = cartStore.items.reduce(
(sum, item) => sum + (item.price * item.quantity), 0
);
const tax = total * 0.1;
const grandTotal = total + tax;
setCartStore({
total,
tax,
grandTotal
});
});
// ローカルストレージとの同期
createEffect(() => {
localStorage.setItem('cart', JSON.stringify(cartStore.items));
});
// アクション
export const cartActions = {
addItem(product: Omit<CartItem, 'quantity'>) {
setCartStore('items', prev => {
const existingIndex = prev.findIndex(item => item.id === product.id);
if (existingIndex >= 0) {
return prev.map((item, index) =>
index === existingIndex
? { ...item, quantity: item.quantity + 1 }
: item
);
} else {
return [...prev, { ...product, quantity: 1 }];
}
});
},
removeItem(id: string) {
setCartStore('items', prev => prev.filter(item => item.id !== id));
},
updateQuantity(id: string, quantity: number) {
if (quantity <= 0) {
this.removeItem(id);
return;
}
setCartStore('items', item => item.id === id, 'quantity', quantity);
},
clearCart() {
setCartStore('items', []);
}
};
export { cartStore };
// パフォーマンス最適化のベストプラクティス
import {
createSignal,
createMemo,
createComputed,
For,
Index,
Show,
Switch,
Match,
batch
} from 'solid-js';
function OptimizedList() {
const [items, setItems] = createSignal([]);
const [filter, setFilter] = createSignal('');
const [sortBy, setSortBy] = createSignal('name');
// 効率的なフィルタリングとソート
const processedItems = createMemo(() => {
const currentItems = items();
const currentFilter = filter().toLowerCase();
const currentSortBy = sortBy();
return currentItems
.filter(item =>
!currentFilter ||
item.name.toLowerCase().includes(currentFilter)
)
.sort((a, b) => {
switch (currentSortBy) {
case 'name':
return a.name.localeCompare(b.name);
case 'date':
return new Date(b.createdAt) - new Date(a.createdAt);
case 'price':
return a.price - b.price;
default:
return 0;
}
});
});
// バッチ更新による最適化
const updateMultipleFilters = (newFilter: string, newSort: string) => {
batch(() => {
setFilter(newFilter);
setSortBy(newSort);
});
};
return (
<div class="optimized-list">
<div class="controls">
<input
type="text"
placeholder="検索..."
value={filter()}
onInput={(e) => setFilter(e.target.value)}
/>
<select
value={sortBy()}
onChange={(e) => setSortBy(e.target.value)}
>
<option value="name">名前順</option>
<option value="date">日付順</option>
<option value="price">価格順</option>
</select>
</div>
{/* キーベースのリストレンダリング */}
<For each={processedItems()}>
{(item) => <OptimizedItem item={item} />}
</For>
{/* インデックスベース(より高速、アイテムが変わらない場合)*/}
<Index each={processedItems()}>
{(item, index) => (
<div class="item-wrapper">
<span class="index">{index + 1}</span>
<OptimizedItem item={item()} />
</div>
)}
</Index>
</div>
);
}
// 条件分岐の最適化
function ConditionalRendering(props) {
return (
<div>
{/* 単純な条件分岐 */}
<Show when={props.user} fallback={<LoginForm />}>
<UserDashboard user={props.user} />
</Show>
{/* 複数の条件分岐 */}
<Switch>
<Match when={props.status === 'loading'}>
<LoadingSpinner />
</Match>
<Match when={props.status === 'error'}>
<ErrorMessage error={props.error} />
</Match>
<Match when={props.status === 'success'}>
<SuccessContent data={props.data} />
</Match>
</Switch>
</div>
);
}
// リソース管理とクリーンアップ
import { onCleanup, createEffect } from 'solid-js';
function WebSocketComponent() {
let ws: WebSocket;
createEffect(() => {
// WebSocket接続
ws = new WebSocket('ws://localhost:8080');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
handleMessage(data);
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
// クリーンアップの登録
onCleanup(() => {
if (ws) {
ws.close();
}
});
});
// DOM イベントリスナーの管理
createEffect(() => {
const handleResize = () => {
console.log('Window resized');
};
window.addEventListener('resize', handleResize);
onCleanup(() => {
window.removeEventListener('resize', handleResize);
});
});
return <div>WebSocket Component</div>;
}
移行対象の選定と技術検証
小規模機能での SolidJS 導入
ページ単位での移行実行
全体最適化と移行完了
// react-to-solid-adapter.ts
// React コンポーネントを SolidJS で使用するためのアダプター
import { createSignal, createEffect, onCleanup } from 'solid-js';
// React useState の SolidJS 版
export function useState<T>(initialValue: T): [() => T, (value: T | ((prev: T) => T)) => void] {
const [signal, setSignal] = createSignal(initialValue);
const setState = (value: T | ((prev: T) => T)) => {
if (typeof value === 'function') {
setSignal(prev => (value as (prev: T) => T)(prev));
} else {
setSignal(value);
}
};
return [signal, setState];
}
// React useEffect の SolidJS 版
export function useEffect(fn: () => void | (() => void), deps?: any[]) {
createEffect(() => {
const cleanup = fn();
if (cleanup && typeof cleanup === 'function') {
onCleanup(cleanup);
}
});
}
// React useMemo の SolidJS 版
export function useMemo<T>(fn: () => T, deps?: any[]): () => T {
return createMemo(fn);
}
// React useCallback の SolidJS 版
export function useCallback<T extends (...args: any[]) => any>(
callback: T,
deps?: any[]
): T {
return createMemo(() => callback);
}
// React版 UserProfile
import React, { useState, useEffect, useMemo } from 'react';
interface User {
id: string;
name: string;
email: string;
avatar: string;
}
const UserProfile: React.FC<{ userId: string }> = ({ userId }) => {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
async function fetchUser() {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new Error('User not found');
const userData = await response.json();
setUser(userData);
} catch (err) {
setError(err instanceof Error ? err.message : 'Error');
} finally {
setLoading(false);
}
}
fetchUser();
}, [userId]);
const displayName = useMemo(() => {
return user ? `${user.name} (${user.email})` : '';
}, [user]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!user) return <div>User not found</div>;
return (
<div className="user-profile">
<img src={user.avatar} alt={user.name} />
<h2>{displayName}</h2>
</div>
);
};
export default UserProfile;
// SolidJS版 UserProfile
import { createResource, createMemo, Show } from 'solid-js';
interface User {
id: string;
name: string;
email: string;
avatar: string;
}
async function fetchUser(userId: string): Promise<User> {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new Error('User not found');
return response.json();
}
interface UserProfileProps {
userId: string;
}
function UserProfile(props: UserProfileProps) {
// createResource で非同期データ取得
const [user] = createResource(
() => props.userId,
fetchUser
);
// 自動的な依存関係追跡
const displayName = createMemo(() => {
const userData = user();
return userData ? `${userData.name} (${userData.email})` : '';
});
return (
<div class="user-profile">
<Show
when={!user.loading}
fallback={<div>Loading...</div>}
>
<Show
when={!user.error}
fallback={<div>Error: {user.error}</div>}
>
<Show
when={user()}
fallback={<div>User not found</div>}
>
{(user) => (
<>
<img src={user().avatar} alt={user().name} />
<h2>{displayName()}</h2>
</>
)}
</Show>
</Show>
</Show>
</div>
);
}
export default UserProfile;
// React版 UserProfile
import React, { useState, useEffect, useMemo } from 'react';
interface User {
id: string;
name: string;
email: string;
avatar: string;
}
const UserProfile: React.FC<{ userId: string }> = ({ userId }) => {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
async function fetchUser() {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new Error('User not found');
const userData = await response.json();
setUser(userData);
} catch (err) {
setError(err instanceof Error ? err.message : 'Error');
} finally {
setLoading(false);
}
}
fetchUser();
}, [userId]);
const displayName = useMemo(() => {
return user ? `${user.name} (${user.email})` : '';
}, [user]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!user) return <div>User not found</div>;
return (
<div className="user-profile">
<img src={user.avatar} alt={user.name} />
<h2>{displayName}</h2>
</div>
);
};
export default UserProfile;
// SolidJS版 UserProfile
import { createResource, createMemo, Show } from 'solid-js';
interface User {
id: string;
name: string;
email: string;
avatar: string;
}
async function fetchUser(userId: string): Promise<User> {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new Error('User not found');
return response.json();
}
interface UserProfileProps {
userId: string;
}
function UserProfile(props: UserProfileProps) {
// createResource で非同期データ取得
const [user] = createResource(
() => props.userId,
fetchUser
);
// 自動的な依存関係追跡
const displayName = createMemo(() => {
const userData = user();
return userData ? `${userData.name} (${userData.email})` : '';
});
return (
<div class="user-profile">
<Show
when={!user.loading}
fallback={<div>Loading...</div>}
>
<Show
when={!user.error}
fallback={<div>Error: {user.error}</div>}
>
<Show
when={user()}
fallback={<div>User not found</div>}
>
{(user) => (
<>
<img src={user().avatar} alt={user().name} />
<h2>{displayName()}</h2>
</>
)}
</Show>
</Show>
</Show>
</div>
);
}
export default UserProfile;
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import solid from 'vite-plugin-solid';
export default defineConfig({
plugins: [solid()],
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./src/test-setup.ts']
}
});
// __tests__/Counter.test.tsx
import { render, fireEvent, screen } from '@solidjs/testing-library';
import '@testing-library/jest-dom';
import Counter from '../components/Counter';
describe('Counter', () => {
test('renders initial count', () => {
render(() => <Counter initialCount={5} />);
expect(screen.getByText('Count: 5')).toBeInTheDocument();
});
test('increments count on button click', async () => {
render(() => <Counter initialCount={0} />);
const button = screen.getByRole('button', { name: /increment/i });
await fireEvent.click(button);
expect(screen.getByText('Count: 1')).toBeInTheDocument();
});
test('updates display name reactively', async () => {
render(() => <Counter initialCount={0} />);
const input = screen.getByLabelText(/name/i);
await fireEvent.input(input, { target: { value: 'Test User' } });
expect(screen.getByText('Hello, Test User!')).toBeInTheDocument();
});
});
// performance.test.ts
import { render } from '@solidjs/testing-library';
import { performance } from 'perf_hooks';
describe('Performance Tests', () => {
test('large list rendering performance', () => {
const items = Array.from({ length: 10000 }, (_, i) => ({
id: i,
name: `Item ${i}`,
value: Math.random()
}));
const startTime = performance.now();
render(() => <LargeList items={items} />);
const endTime = performance.now();
const renderTime = endTime - startTime;
// 10,000 アイテムのレンダリングが100ms以内で完了することを確認
expect(renderTime).toBeLessThan(100);
});
test('rapid updates performance', async () => {
const { container } = render(() => <RapidUpdateComponent />);
const startTime = performance.now();
// 1000回の連続更新をシミュレート
for (let i = 0; i < 1000; i++) {
const button = container.querySelector('button');
await fireEvent.click(button);
}
const endTime = performance.now();
const updateTime = endTime - startTime;
// 1000回の更新が500ms以内で完了することを確認
expect(updateTime).toBeLessThan(500);
});
});
count
ではなく count()
で値を取得SolidJS では「再レンダリング」という概念自体が存在しません。変更があった部分だけが更新され、残りは一切触れられることがありません。これが真のリアクティビティです。
// ベストプラクティス集
import { createSignal, createMemo, createStore, batch } from 'solid-js';
// ✅ 良い例
function GoodPractices() {
const [count, setCount] = createSignal(0);
const [multiplier, setMultiplier] = createSignal(2);
// 計算された値はmemoを使用
const result = createMemo(() => count() * multiplier());
// バッチ更新で不要な再計算を防ぐ
const updateBoth = () => {
batch(() => {
setCount(c => c + 1);
setMultiplier(m => m + 1);
});
};
// 条件付きEffect(依存関係が変わった時のみ実行)
createEffect(() => {
if (count() > 10) {
console.log('Count exceeded 10');
}
});
return (
<div>
<p>Result: {result()}</p>
<button onClick={updateBoth}>Update Both</button>
</div>
);
}
// ❌ 悪い例
function BadPractices() {
const [count, setCount] = createSignal(0);
// 毎回新しい関数を作成(非効率)
const handleClick = () => setCount(count() + 1);
// 不要な計算の繰り返し
return (
<div>
<p>Count: {count()}</p>
<p>Double: {count() * 2}</p> {/* memoを使うべき */}
<p>Triple: {count() * 3}</p> {/* memoを使うべき */}
<button onClick={handleClick}>+1</button>
</div>
);
}
SolidJS は 2025 年において、フロントエンド開発の新たなスタンダードとして確固たる地位を築いています。React比 3 倍の高速化を実現しながらも学習コストを最小限に抑えた設計は、多くの開発者にとって魅力的な選択肢となっています。
SolidJSの主要な利点:
採用を検討すべきケース:
2025 年後半から 2026 年にかけて、SolidJS はさらなる成長が予想されます。エンタープライズ向け機能の充実、エコシステムの拡大、そして大手企業での採用事例増加により、Reactの強力な代替選択肢として位置づけられることでしょう。
フロントエンド開発の未来を見据えた技術選択として、SolidJS は間違いなく検討に値するフレームワークです。