ソース情報
- リポジトリ
- tools-only/X-Skills
- ソースの最終更新活動
- 2026年3月1日 03:37
- 検出された SKILL.md の言語
- 英語
- スター
- 7
- フォーク
- 1
インストール方法
デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。
ソースファイルを確認
インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。
メニュー
デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。
インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
直接コマンドでは確認用 Prompt が省略されます。実行前にソースを確認してください。
npx skills add https://github.com/tools-only/X-Skills --skill frontend-react-state-managementコマンドは1行のまま表示されます。コピー前に横へスクロールして全体を確認してください。
ローカルで確認しますか?SkillsMP が現在取得できるファイルをダウンロードできます。
Index of Build Systems Skills
Coordination patterns for distributed dataflow systems including barriers, epochs, and distributed snapshots
Windowing, sessionization, time-series aggregation, and late data handling for streaming systems
SOC 職業分類に基づく
SKILL.md を表示中
| name | frontend-react-state-management |
| description | Choosing state management solution |
Scope: Context, Zustand, Jotai, Redux Toolkit, when to use each, state patterns Lines: ~300 Last Updated: 2025-10-18
Activate this skill when:
Local State - Component-specific
const [count, setCount] = useState(0);
Shared State - Multiple components
const theme = useContext(ThemeContext);
Global State - Application-wide
const user = useStore(state => state.user);
Server State - Data from API
const { data: posts } = useSWR('/api/posts');
State needed by single component? → useState
↓
State needed by 2-3 nearby components? → Lift state up or useContext
↓
State needed across app (5+ components)? → Zustand or Jotai
↓
Complex state logic (reducers, middleware)? → Redux Toolkit
↓
Server data (API, caching, revalidation)? → SWR or React Query
| Library | Complexity | Bundle Size | DevTools | Best For |
|---|---|---|---|---|
| Context | Low | 0kb (built-in) | No | Small apps, theming |
| Zustand | Low | 1.2kb | Yes | Most apps, simple global state |
| Jotai | Medium | 3kb | Yes | Atomic state, derived values |
| Redux Toolkit | High | 8kb | Excellent | Large apps, complex logic |
// contexts/ThemeContext.tsx
import { createContext, useContext, useState } from 'react';
type Theme = 'light' | 'dark';
const ThemeContext = createContext<{
theme: Theme;
setTheme: (theme: Theme) => void;
} | null>(null);
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<Theme>('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) throw new Error('useTheme must be used within ThemeProvider');
return context;
}
// Usage
function App() {
(
);
}
() {
{ theme, setTheme } = ();
(
);
}
// Split context to prevent unnecessary re-renders
const UserContext = createContext<User | null>(null);
const UserActionsContext = createContext<{
login: (email: string, password: string) => void;
logout: () => void;
} | null>(null);
function UserProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null);
// Memoize actions to prevent re-renders
const actions = useMemo(() => ({
login: async (email: string, password: string) => {
const user = await loginAPI(email, password);
setUser(user);
},
logout: () => setUser(null),
}), []);
return (
<UserContext.Provider value={user}>
{children}
);
}
() {
user = ();
;
}
() {
{ logout } = ()!;
;
}
When to use Context:
When NOT to use Context:
// stores/useStore.ts
import { create } from 'zustand';
interface Todo {
id: string;
title: string;
completed: boolean;
}
interface TodoStore {
todos: Todo[];
addTodo: (title: string) => void;
toggleTodo: (id: string) => void;
deleteTodo: (id: string) => void;
}
export const useTodoStore = create<TodoStore>((set) => ({
todos: [],
addTodo: (title) =>
set((state) => ({
todos: [...state.todos, { id: crypto.randomUUID(), title, completed: false }],
})),
toggleTodo: (id) =>
set( ({
: state..(
todo. === id ? { ...todo, : !todo. } : todo
),
})),
:
( ({
: state..( todo. !== id),
})),
}));
() {
todos = ( state.);
toggleTodo = ( state.);
(
);
}
() {
addTodo = ( state.);
(
);
}
Selector optimization - Component only re-renders when selected value changes:
// ❌ Re-renders on any store change
const store = useTodoStore();
// ✅ Only re-renders when todos change
const todos = useTodoStore((state) => state.todos);
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';
interface Store {
nested: {
deeply: {
value: number;
};
};
increment: () => void;
}
const useStore = create<Store>()(
immer((set) => ({
nested: { deeply: { value: 0 } },
// Immer allows "mutation" syntax
increment: () =>
set((state) => {
state.nested.deeply.value += 1;
}),
}))
);
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface AuthStore {
user: User | null;
login: (user: User) => void;
logout: () => void;
}
export const useAuthStore = create<AuthStore>()(
persist(
(set) => ({
user: null,
login: (user) => set({ user }),
logout: () => set({ user: null }),
}),
{
name: 'auth-storage', // localStorage key
}
)
);
When to use Zustand:
// stores/atoms.ts
import { atom } from 'jotai';
// Primitive atoms
export const countAtom = atom(0);
export const userAtom = atom<User | null>(null);
// Derived atoms (computed)
export const doubleCountAtom = atom((get) => get(countAtom) * 2);
// Writable derived atoms
export const incrementCountAtom = atom(
(get) => get(countAtom),
(get, set) => set(countAtom, get(countAtom) + 1)
);
// Async atoms
export const postsAtom = atom(async () => {
const res = await fetch('/api/posts');
return res.json();
});
// Usage
function Counter() {
const [count, setCount] = useAtom(countAtom);
const doubleCount = (doubleCountAtom);
(
);
}
import { atomFamily } from 'jotai/utils';
// Create atoms dynamically by ID
const todoAtomFamily = atomFamily((id: string) =>
atom<Todo>({ id, title: '', completed: false })
);
function TodoItem({ id }: { id: string }) {
const [todo, setTodo] = useAtom(todoAtomFamily(id));
return (
<div>
<input
value={todo.title}
onChange={(e) => setTodo({ ...todo, title: e.target.value })}
/>
</div>
);
}
import { atomWithStorage } from 'jotai/utils';
export const themeAtom = atomWithStorage<'light' | 'dark'>('theme', 'light');
When to use Jotai:
// store/store.ts
import { configureStore } from '@reduxjs/toolkit';
import todosReducer from './todosSlice';
import userReducer from './userSlice';
export const store = configureStore({
reducer: {
todos: todosReducer,
user: userReducer,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
// store/todosSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface Todo {
id: string;
title: string;
completed: boolean;
}
interface TodosState {
items: Todo[];
loading: boolean;
}
const initialState: TodosState = {
items: [],
loading: false,
};
const todosSlice = createSlice({
name: 'todos',
initialState,
reducers: {
addTodo: (state, action: PayloadAction<string>) => {
state.items.push({
id: crypto.randomUUID(),
title: action.payload,
completed: false,
});
},
toggleTodo: (state, action: PayloadAction<string>) => {
const todo = state.items.find( t. === action.);
(todo) todo. = !todo.;
},
: {
state. = state..( t. !== action.);
},
},
});
{ addTodo, toggleTodo, deleteTodo } = todosSlice.;
todosSlice.;
// store/userSlice.ts
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
export const fetchUser = createAsyncThunk('user/fetch', async (userId: string) => {
const res = await fetch(`/api/users/${userId}`);
return res.json();
});
interface UserState {
user: User | null;
loading: boolean;
error: string | null;
}
const userSlice = createSlice({
name: 'user',
initialState: { user: null, loading: false, error: null } as UserState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => {
state.loading = true;
state.error = null;
})
.(fetchUser., {
state. = ;
state. = action.;
})
.(fetchUser., {
state. = ;
state. = action.. || ;
});
},
});
userSlice.;
// hooks/redux.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import type { RootState, AppDispatch } from '../store/store';
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
// Usage
function TodoList() {
const todos = useAppSelector((state) => state.todos.items);
const dispatch = useAppDispatch();
return (
<div>
{todos.map((todo) => (
<div key={todo.id} onClick={() => dispatch(toggleTodo(todo.id))}>
{todo.title}
</div>
))}
<button onClick={() => dispatch(addTodo('New Todo'))}>Add</button>
</div>
);
}
When to use Redux Toolkit:
// Zustand
const useStore = create<Store>((set) => ({
todos: [],
toggleTodo: async (id: string) => {
// Optimistic update
set((state) => ({
todos: state.todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
),
}));
try {
await fetch(`/api/todos/${id}/toggle`, { method: 'POST' });
} catch (error) {
// Revert on error
set((state) => ({
todos: state.todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
),
}));
}
},
}));
interface Store {
data: Data[];
loading: boolean;
error: string | null;
fetch: () => Promise<void>;
}
const useStore = create<Store>((set) => ({
data: [],
loading: false,
error: null,
fetch: async () => {
set({ loading: true, error: null });
try {
const res = await fetch('/api/data');
const data = await res.json();
set({ data, loading: false });
} catch (error) {
set({ error: error.message, loading: false });
}
},
}));
// Jotai - automatic memoization
const todosAtom = atom<Todo[]>([]);
const completedTodosAtom = atom((get) =>
get(todosAtom).filter((todo) => todo.completed)
);
const activeTodosAtom = atom((get) =>
get(todosAtom).filter((todo) => !todo.completed)
);
const statsAtom = atom((get) => {
const todos = get(todosAtom);
return {
total: todos.length,
completed: get(completedTodosAtom).length,
active: get(activeTodosAtom).length,
};
});
// Zustand - manual memoization
const useStore = create<Store>((set) => ({
todos: [],
get completedTodos() {
return this.todos.filter((todo) => todo.completed);
},
}));
// Before (Context)
const TodoContext = createContext<{
todos: Todo[];
addTodo: (title: string) => void;
} | null>(null);
function TodoProvider({ children }) {
const [todos, setTodos] = useState<Todo[]>([]);
const addTodo = (title: string) => setTodos([...todos, { title }]);
return <TodoContext.Provider value={{ todos, addTodo }}>{children}</TodoContext.Provider>;
}
// After (Zustand)
const useTodoStore = create<Store>((set) => ({
todos: [],
addTodo: (title) => set((state) => ({ todos: [...state.todos, { title }] })),
}));
// Replace useContext with store hook
const todos = useTodoStore((state) => state.todos);
addTodo = ( state.);
Local component state → useState
2-3 components nearby → Lift state or Context
App-wide, simple → Zustand
Atomic, derived state → Jotai
Complex, large app → Redux Toolkit
Server data → SWR/React Query
// ❌ Bad: Entire store subscription
const store = useStore();
// ✅ Good: Selective subscription
const todos = useStore((state) => state.todos);
// ✅ Good: Multiple selectors
const addTodo = useStore((state) => state.addTodo);
const deleteTodo = useStore((state) => state.deleteTodo);
// ✅ Good: Memoized selector
const completedCount = useStore(
(state) => state.todos.filter((t) => t.completed).length
);
❌ Storing server state in global store: Use SWR/React Query instead ✅ Keep server state separate from client state
❌ Too much global state: Most state should be local ✅ Only globalize what needs to be shared
❌ Not splitting context: One context causes all consumers to re-render ✅ Split into multiple contexts (data + actions)
❌ Derived state not memoized: Recalculates on every render ✅ Use useMemo, Jotai derived atoms, or Zustand getters
Located in resources/scripts/:
analyze_state.py - Analyze React component state patterns
./analyze_state.py ./src
./analyze_state.py ./src --json
benchmark_renders.js - Benchmark render performance
./benchmark_renders.js
./benchmark_renders.js --depth 20 --updates 500
./benchmark_renders.js --json
detect_unnecessary_renders.js - Detect unnecessary re-renders
./detect_unnecessary_renders.js ./src
./detect_unnecessary_renders.js ./components --json
Located in resources/examples/typescript/:
zustand-store.ts - Complete Zustand patterns
tanstack-query-hooks.ts - TanStack Query (React Query) patterns
context-provider.tsx - React Context patterns
react-hook-form-example.tsx - Form state management
immutable-updates.ts - Immutable state patterns
react-component-patterns.md - useState, useContext, custom hooksreact-data-fetching.md - SWR, React Query for server statenextjs-app-router.md - Server Components (minimal client state)frontend-performance.md - Re-render optimizationLast Updated: 2025-10-27 Format Version: 1.0 (Atomic)