重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
react-patterns by manutej/luxor-claude-marketplace
npx skills add https://github.com/manutej/luxor-claude-marketplace --skill react-patterns一项全面掌握现代 React 开发模式的技能,包括 React 18+ 特性、钩子、组件组合、状态管理策略以及构建可扩展 Web 应用程序的性能优化技术。
在以下情况下使用此技能:
现代 React 强调:
"use client" 的交互式组件广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
管理简单值的本地组件状态。
语法:
const [state, setState] = useState(initialValue);
基础示例:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(prev => prev + 1)}>Increment (functional)</button>
</div>
);
}
最佳实践:
多个状态变量:
function Form() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [age, setAge] = useState(0);
return (
<form>
<input value={name} onChange={e => setName(e.target.value)} />
<input value={email} onChange={e => setEmail(e.target.value)} />
<input type="number" value={age} onChange={e => setAge(Number(e.target.value))} />
</form>
);
}
惰性初始化:
function ExpensiveComponent() {
// 函数仅在初始渲染时运行一次
const [state, setState] = useState(() => {
const initialValue = expensiveComputation();
return initialValue;
});
return <div>{state}</div>;
}
使用 reducer 模式管理复杂的状态逻辑(类似于 Redux)。
语法:
const [state, dispatch] = useReducer(reducer, initialState, init?);
任务管理器示例:
import { useReducer } from 'react';
function tasksReducer(tasks, action) {
switch (action.type) {
case 'added':
return [...tasks, {
id: action.id,
text: action.text,
done: false
}];
case 'changed':
return tasks.map(t =>
t.id === action.task.id ? action.task : t
);
case 'deleted':
return tasks.filter(t => t.id !== action.id);
default:
throw Error('Unknown action: ' + action.type);
}
}
function TaskApp() {
const [tasks, dispatch] = useReducer(tasksReducer, []);
function handleAddTask(text) {
dispatch({
type: 'added',
id: Date.now(),
text: text,
});
}
function handleChangeTask(task) {
dispatch({
type: 'changed',
task: task
});
}
function handleDeleteTask(taskId) {
dispatch({
type: 'deleted',
id: taskId
});
}
return (
<>
<AddTask onAddTask={handleAddTask} />
<TaskList
tasks={tasks}
onChangeTask={handleChangeTask}
onDeleteTask={handleDeleteTask}
/>
</>
);
}
何时使用 useReducer:
无需 prop 钻取即可访问上下文值。
语法:
const value = useContext(SomeContext);
主题上下文示例:
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext(null);
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
// 用法
function Button() {
const { theme, toggleTheme } = useTheme();
return (
<button
onClick={toggleTheme}
className={theme === 'dark' ? 'btn-dark' : 'btn-light'}
>
Toggle Theme
</button>
);
}
结合 useReducer + useContext:
import { createContext, useContext, useReducer } from 'react';
const TasksContext = createContext(null);
const TasksDispatchContext = createContext(null);
export function TasksProvider({ children }) {
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
return (
<TasksContext.Provider value={tasks}>
<TasksDispatchContext.Provider value={dispatch}>
{children}
</TasksDispatchContext.Provider>
</TasksContext.Provider>
);
}
export function useTasks() {
return useContext(TasksContext);
}
export function useTasksDispatch() {
return useContext(TasksDispatchContext);
}
// 在组件中的用法
function TaskList() {
const tasks = useTasks();
const dispatch = useTasksDispatch();
return (
<ul>
{tasks.map(task => (
<li key={task.id}>
{task.text}
<button onClick={() => dispatch({ type: 'deleted', id: task.id })}>
Delete
</button>
</li>
))}
</ul>
);
}
将组件与外部系统(API、DOM、订阅)同步。
语法:
useEffect(() => {
// 副作用逻辑
return () => {
// 清理逻辑
};
}, [dependencies]);
数据获取示例:
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let ignore = false; // 防止竞态条件
async function fetchUser() {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
if (!ignore) {
setUser(data);
}
} catch (err) {
if (!ignore) {
setError(err.message);
}
} finally {
if (!ignore) {
setLoading(false);
}
}
}
fetchUser();
return () => {
ignore = true; // 清理
};
}, [userId]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>User: {user?.name}</div>;
}
订阅示例:
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const connection = createConnection(roomId);
connection.on('message', (msg) => {
setMessages(prev => [...prev, msg]);
});
connection.connect();
return () => {
connection.disconnect(); // 在卸载或 roomId 更改时清理
};
}, [roomId]);
return (
<div>
{messages.map((msg, i) => (
<div key={i}>{msg}</div>
))}
</div>
);
}
常见副作用模式:
// 在挂载时运行一次
useEffect(() => {
console.log('Component mounted');
}, []); // 空依赖数组
// 在每次渲染时运行
useEffect(() => {
console.log('Component rendered');
}); // 无依赖数组
// 当特定值更改时运行
useEffect(() => {
console.log('userId changed:', userId);
}, [userId]); // 包含值的依赖数组
// 在卸载时清理
useEffect(() => {
const timer = setTimeout(() => {
console.log('Delayed action');
}, 1000);
return () => clearTimeout(timer);
}, []);
useEffect 的同步版本,在浏览器绘制之前运行。
使用场景:
示例:
import { useLayoutEffect, useRef, useState } from 'react';
function Tooltip() {
const ref = useRef(null);
const [tooltipHeight, setTooltipHeight] = useState(0);
useLayoutEffect(() => {
const { height } = ref.current.getBoundingClientRect();
setTooltipHeight(height); // 在绘制前同步更新
}, []);
return (
<div ref={ref}>
Tooltip content (height: {tooltipHeight}px)
</div>
);
}
在渲染之间缓存昂贵的计算。
语法:
const cachedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
示例:
import { useMemo } from 'react';
function TodoList({ todos, filter }) {
// 仅当 todos 或 filter 更改时重新计算
const visibleTodos = useMemo(() => {
console.log('Filtering todos...');
return todos.filter(todo => {
if (filter === 'all') return true;
if (filter === 'active') return !todo.done;
if (filter === 'completed') return todo.done;
return true;
});
}, [todos, filter]);
return (
<ul>
{visibleTodos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
何时使用 useMemo:
反模式(不必要的):
// ❌ 不要用于简单计算
const sum = useMemo(() => a + b, [a, b]); // 过度设计
// ✅ 直接计算即可
const sum = a + b;
在渲染之间记忆化函数引用。
语法:
const cachedFn = useCallback(() => {
// 函数逻辑
}, [dependencies]);
示例:
import { useState, useCallback, memo } from 'react';
const TodoItem = memo(function TodoItem({ todo, onChange, onDelete }) {
console.log('TodoItem rendered:', todo.id);
return (
<li>
<input
type="checkbox"
checked={todo.done}
onChange={() => onChange(todo)}
/>
{todo.text}
<button onClick={() => onDelete(todo.id)}>Delete</button>
</li>
);
});
function TodoList() {
const [todos, setTodos] = useState([]);
// 记忆化处理函数以防止 TodoItem 重新渲染
const handleChange = useCallback((todo) => {
setTodos(prev => prev.map(t =>
t.id === todo.id ? { ...t, done: !t.done } : t
));
}, []);
const handleDelete = useCallback((id) => {
setTodos(prev => prev.filter(t => t.id !== id));
}, []);
return (
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onChange={handleChange}
onDelete={handleDelete}
/>
))}
</ul>
);
}
何时使用 useCallback:
记忆化整个组件以防止不必要的重新渲染。
语法:
const MemoizedComponent = memo(function Component(props) {
// 组件逻辑
}, arePropsEqual?);
示例:
import { memo } from 'react';
const ExpensiveComponent = memo(function ExpensiveComponent({ data, onClick }) {
console.log('ExpensiveComponent rendered');
return (
<div>
{data.map(item => (
<div key={item.id} onClick={() => onClick(item)}>
{item.name}
</div>
))}
</div>
);
});
// 使用自定义比较
const CustomMemoComponent = memo(
function CustomMemoComponent({ user }) {
return <div>{user.name}</div>;
},
(prevProps, nextProps) => {
// 如果 props 相等则返回 true(跳过重新渲染)
return prevProps.user.id === nextProps.user.id;
}
);
创建在渲染之间持久存在的可变引用。
语法:
const ref = useRef(initialValue);
DOM 引用示例:
import { useRef } from 'react';
function VideoPlayer({ src, isPlaying }) {
const videoRef = useRef(null);
useEffect(() => {
if (isPlaying) {
videoRef.current.play();
} else {
videoRef.current.pause();
}
}, [isPlaying]);
return <video ref={videoRef} src={src} />;
}
存储可变值:
function Timer() {
const intervalRef = useRef(null);
const [count, setCount] = useState(0);
function start() {
if (intervalRef.current) return; // 已在运行
intervalRef.current = setInterval(() => {
setCount(c => c + 1);
}, 1000);
}
function stop() {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
}
return (
<div>
<p>Count: {count}</p>
<button onClick={start}>Start</button>
<button onClick={stop}>Stop</button>
</div>
);
}
先前值模式:
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
// 用法
function Counter({ count }) {
const prevCount = usePrevious(count);
return (
<div>
<p>Current: {count}</p>
<p>Previous: {prevCount}</p>
</div>
);
}
将状态更新标记为非紧急,保持 UI 响应性。
语法:
const [isPending, startTransition] = useTransition();
示例:
import { useState, useTransition } from 'react';
function SearchResults() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
function handleChange(e) {
const value = e.target.value;
setQuery(value); // 紧急更新(输入值)
startTransition(() => {
// 非紧急更新(搜索结果)
const filtered = performExpensiveSearch(value);
setResults(filtered);
});
}
return (
<div>
<input value={query} onChange={handleChange} />
{isPending && <div>Searching...</div>}
<ul>
{results.map(result => (
<li key={result.id}>{result.name}</li>
))}
</ul>
</div>
);
}
延迟更新部分 UI。
语法:
const deferredValue = useDeferredValue(value);
示例:
import { useState, useDeferredValue, memo } from 'react';
const SlowList = memo(function SlowList({ items }) {
// 故意缓慢渲染
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
});
function App() {
const [text, setText] = useState('');
const deferredText = useDeferredValue(text);
return (
<>
<input value={text} onChange={e => setText(e.target.value)} />
<SlowList items={filterItems(deferredText)} />
</>
);
}
管理服务器操作状态和挂起状态。
语法:
const [state, formAction, isPending] = useActionState(serverAction, initialState);
示例:
"use client";
import { useActionState } from 'react';
import { updateName } from './actions';
function UpdateNameForm() {
const [state, submitAction, isPending] = useActionState(
updateName,
{ error: null }
);
return (
<form action={submitAction}>
<input type="text" name="name" disabled={isPending} />
{state.error && <span className="error">{state.error}</span>}
<button type="submit" disabled={isPending}>
{isPending ? 'Updating...' : 'Update'}
</button>
</form>
);
}
服务器操作 (actions.js):
"use server";
export async function updateName(prevState, formData) {
const name = formData.get('name');
if (!name) {
return { error: 'Name is required' };
}
try {
await db.users.updateName(name);
return { error: null };
} catch (err) {
return { error: 'Failed to update name' };
}
}
自定义钩子允许您在组件之间提取和重用有状态的逻辑。
始终以 use 为前缀命名自定义钩子:
useCustomHook // ✅ 正确
customHook // ❌ 错误
import { useState, useEffect } from 'react';
export function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let ignore = false;
async function fetchData() {
try {
setLoading(true);
setError(null);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (!ignore) {
setData(result);
}
} catch (err) {
if (!ignore) {
setError(err.message);
}
} finally {
if (!ignore) {
setLoading(false);
}
}
}
fetchData();
return () => {
ignore = true;
};
}, [url]);
return { data, loading, error };
}
// 用法
function UserProfile({ userId }) {
const { data: user, loading, error } = useFetch(`/api/users/${userId}`);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>{user.name}</div>;
}
import { useState } from 'react';
export function useFormInput(initialValue = '') {
const [value, setValue] = useState(initialValue);
function handleChange(e) {
setValue(e.target.value);
}
function reset() {
setValue(initialValue);
}
return {
value,
onChange: handleChange,
reset
};
}
// 用法
function LoginForm() {
const email = useFormInput('');
const password = useFormInput('');
function handleSubmit(e) {
e.preventDefault();
console.log('Email:', email.value);
console.log('Password:', password.value);
email.reset();
password.reset();
}
return (
<form onSubmit={handleSubmit}>
<input type="email" {...email} placeholder="Email" />
<input type="password" {...password} placeholder="Password" />
<button type="submit">Login</button>
</form>
);
}
import { useState, useEffect } from 'react';
export function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
useEffect(() => {
try {
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error(error);
}
}, [key, value]);
return [value, setValue];
}
// 用法
function Settings() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
const [language, setLanguage] = useLocalStorage('language', 'en');
return (
<div>
<select value={theme} onChange={e => setTheme(e.target.value)}>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
<select value={language} onChange={e => setLanguage(e.target.value)}>
<option value="en">English</option>
<option value="es">Spanish</option>
</select>
</div>
);
}
import { useState, useEffect } from 'react';
export function useDebounce(value, delay = 500) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
// 用法
function SearchComponent() {
const [searchTerm, setSearchTerm] = useState('');
const debouncedSearchTerm = useDebounce(searchTerm, 500);
useEffect(() => {
if (debouncedSearchTerm) {
// 执行搜索
console.log('Searching for:', debouncedSearchTerm);
}
}, [debouncedSearchTerm]);
return (
<input
type="text"
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
placeholder="Search..."
/>
);
}
import { useState, useEffect } from 'react';
export function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: undefined,
height: undefined,
});
useEffect(() => {
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
handleResize(); // 设置初始大小
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return windowSize;
}
// 用法
function ResponsiveComponent() {
const { width, height } = useWindowSize();
return (
<div>
<p>Window width: {width}px</p>
<p>Window height: {height}px</p>
{width < 768 ? <MobileView /> : <DesktopView />}
</div>
);
}
import { useState, useEffect } from 'react';
export function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
function handleOnline() {
setIsOnline(true);
}
function handleOffline() {
setIsOnline(false);
}
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
}
// 用法
function StatusBar() {
const isOnline = useOnlineStatus();
return (
<div className={isOnline ? 'online' : 'offline'}>
{isOnline ? '✅ Online' : '❌ Offline'}
</div>
);
}
构建共享隐式状态并协同工作的组件。
import { createContext, useContext, useState } from 'react';
const TabsContext = createContext();
export function Tabs({ children, defaultValue }) {
const [activeTab, setActiveTab] = useState(defaultValue);
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
<div className="tabs">{children}</div>
</TabsContext.Provider>
);
}
export function TabList({ children }) {
return <div className="tab-list">{children}</div>;
}
export function Tab({ value, children }) {
const { activeTab, setActiveTab } = useContext(TabsContext);
const isActive = activeTab === value;
return (
<button
className={`tab ${isActive ? 'active' : ''}`}
onClick={() => setActiveTab(value)}
>
{children}
</button>
);
}
export function TabPanels({ children }) {
return <div className="tab-panels">{children}</div>;
}
export function TabPanel({ value, children }) {
const { activeTab } = useContext(TabsContext);
if (activeTab !== value) return null;
return <div className="tab-panel">{children}</div>;
}
// 用法
function App() {
return (
<Tabs defaultValue="tab1">
<TabList>
<Tab value="tab1">Tab 1</Tab>
<Tab value="tab2">Tab 2</Tab>
<Tab value="tab3">Tab 3</Tab>
</TabList>
<TabPanels>
<TabPanel value="tab1">Content for Tab 1</TabPanel>
<TabPanel value="tab2">Content for Tab 2</TabPanel>
<TabPanel value="tab3">Content for Tab 3</TabPanel>
</TabPanels>
</Tabs>
);
}
使用值为函数的 prop 在组件之间共享代码。
function DataFetcher({ url, render }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
});
}, [url]);
return render({ data, loading });
}
// 用法
function App() {
return (
<DataFetcher
url="/api/users"
render={({ data, loading }) => (
loading ? <div>Loading...</div> : (
<ul>
{data?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
)}
/>
);
}
包装组件以增强功能。
import { useEffect, useState } from 'react';
// 用于数据获取的 HOC
function withData(Component, url) {
return function WithDataComponent(props) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
});
}, []);
return <Component {...props} data={data} loading={loading} />;
};
}
// 原始组件
function UserList({ data, loading }) {
if (loading) return <div>Loading...</div>;
return (
<ul>
{data?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
// 增强的组件
const UserListWithData = withData(UserList, '/api/users');
// 用法
function App() {
return <UserListWithData />;
}
将逻辑与展示分离。
// 容器(逻辑)
function UserListContainer() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [searchTerm, setSearchTerm] = useState('');
useEffect(() => {
fetchUsers().then(data => {
setUsers(data);
setLoading(false);
});
}, []);
const filteredUsers = users.filter(user =>
user.name.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<UserListPresenter
users={filteredUsers}
loading={loading}
searchTerm={searchTerm}
onSearchChange={setSearchTerm}
/>
);
}
// 展示器(UI)
function UserListPresenter({ users, loading, searchTerm, onSearchChange }) {
if (loading) return <div>Loading...</div>;
return (
<div>
<input
type="text"
value={searchTerm}
onChange={e => onSearchChange(e.target.value)}
placeholder="Search users..."
/>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
使用 React DevTools Profiler 识别:
import { memo, useMemo, useCallback } from 'react';
const ExpensiveList = memo(function ExpensiveList({ items, onItemClick }) {
console.log('Rendering ExpensiveList');
return (
<ul>
{items.map(item => (
<li key={item.id} onClick={() => onItemClick(item.id)}>
{item.name}
</li>
))}
</ul>
);
});
function App() {
const [items, setItems] = useState([]);
const [count, setCount] = useState(0);
// 记忆化昂贵的计算
const sortedItems = useMemo(() => {
console.log('Sorting items...');
return [...items].sort((a, b) => a.name.localeCompare(b.name));
}, [items]);
// 记忆化回调
const handleItemClick = useCallback((id) => {
console.log('Item clicked:', id);
}, []);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
<ExpensiveList items={sortedItems} onItemClick={handleItemClick} />
</div>
);
}
import { lazy, Suspense } from 'react';
// 懒加载组件
const HeavyComponent = lazy(() => import('./HeavyComponent'));
const Dashboard = lazy(() => import('./Dashboard'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}
// 基于路由的分割
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
import { FixedSizeList } from 'react-window';
function VirtualizedList({ items }) {
const Row = ({ index, style }) => (
<div style={style}>
{items[index].name}
</div>
);
return (
<FixedSizeList
height={500}
itemCount={items.length}
itemSize={50}
width="100%"
>
{Row}
</FixedSizeList>
);
}
// ❌ 不好 - 每次渲染都创建新函数
function List({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id} onClick={() => console.log(item.id)}>
{item.name}
</li>
))}
</ul>
);
}
// ✅ 好 - 使用 useCallback
function List({ items }) {
const handleClick = useCallback((id) => {
console.log(id);
}, []);
return (
<ul>
{items.map(item => (
<li key={item.id} onClick={() => handleClick(item.id)}>
{item.name}
</li>
))}
</ul>
);
}
React 服务器组件在服务器上渲染,减少客户端包大小。
// app/users/page.js (默认是服务器组件)
async function UsersPage() {
// 直接在服务器上获取数据
const users = await db.users.findAll();
return (
<div>
<h1>Users</h1>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div
A comprehensive skill for mastering modern React development patterns, including React 18+ features, hooks, component composition, state management strategies, and performance optimization techniques for building scalable web applications.
Use this skill when:
Modern React emphasizes:
"use client"Manage local component state for simple values.
Syntax:
const [state, setState] = useState(initialValue);
Basic Example:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(prev => prev + 1)}>Increment (functional)</button>
</div>
);
}
Best Practices:
Multiple State Variables:
function Form() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [age, setAge] = useState(0);
return (
<form>
<input value={name} onChange={e => setName(e.target.value)} />
<input value={email} onChange={e => setEmail(e.target.value)} />
<input type="number" value={age} onChange={e => setAge(Number(e.target.value))} />
</form>
);
}
Lazy Initialization:
function ExpensiveComponent() {
// Function only runs once on initial render
const [state, setState] = useState(() => {
const initialValue = expensiveComputation();
return initialValue;
});
return <div>{state}</div>;
}
Manage complex state logic with a reducer pattern (similar to Redux).
Syntax:
const [state, dispatch] = useReducer(reducer, initialState, init?);
Task Manager Example:
import { useReducer } from 'react';
function tasksReducer(tasks, action) {
switch (action.type) {
case 'added':
return [...tasks, {
id: action.id,
text: action.text,
done: false
}];
case 'changed':
return tasks.map(t =>
t.id === action.task.id ? action.task : t
);
case 'deleted':
return tasks.filter(t => t.id !== action.id);
default:
throw Error('Unknown action: ' + action.type);
}
}
function TaskApp() {
const [tasks, dispatch] = useReducer(tasksReducer, []);
function handleAddTask(text) {
dispatch({
type: 'added',
id: Date.now(),
text: text,
});
}
function handleChangeTask(task) {
dispatch({
type: 'changed',
task: task
});
}
function handleDeleteTask(taskId) {
dispatch({
type: 'deleted',
id: taskId
});
}
return (
<>
<AddTask onAddTask={handleAddTask} />
<TaskList
tasks={tasks}
onChangeTask={handleChangeTask}
onDeleteTask={handleDeleteTask}
/>
</>
);
}
When to use useReducer:
Access context values without prop drilling.
Syntax:
const value = useContext(SomeContext);
Theme Context Example:
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext(null);
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{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 Button() {
const { theme, toggleTheme } = useTheme();
return (
<button
onClick={toggleTheme}
className={theme === 'dark' ? 'btn-dark' : 'btn-light'}
>
Toggle Theme
</button>
);
}
Combining useReducer + useContext:
import { createContext, useContext, useReducer } from 'react';
const TasksContext = createContext(null);
const TasksDispatchContext = createContext(null);
export function TasksProvider({ children }) {
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
return (
<TasksContext.Provider value={tasks}>
<TasksDispatchContext.Provider value={dispatch}>
{children}
</TasksDispatchContext.Provider>
</TasksContext.Provider>
);
}
export function useTasks() {
return useContext(TasksContext);
}
export function useTasksDispatch() {
return useContext(TasksDispatchContext);
}
// Usage in components
function TaskList() {
const tasks = useTasks();
const dispatch = useTasksDispatch();
return (
<ul>
{tasks.map(task => (
<li key={task.id}>
{task.text}
<button onClick={() => dispatch({ type: 'deleted', id: task.id })}>
Delete
</button>
</li>
))}
</ul>
);
}
Synchronize component with external systems (APIs, DOM, subscriptions).
Syntax:
useEffect(() => {
// Effect logic
return () => {
// Cleanup logic
};
}, [dependencies]);
Data Fetching Example:
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let ignore = false; // Prevent race conditions
async function fetchUser() {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
if (!ignore) {
setUser(data);
}
} catch (err) {
if (!ignore) {
setError(err.message);
}
} finally {
if (!ignore) {
setLoading(false);
}
}
}
fetchUser();
return () => {
ignore = true; // Cleanup
};
}, [userId]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>User: {user?.name}</div>;
}
Subscription Example:
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const connection = createConnection(roomId);
connection.on('message', (msg) => {
setMessages(prev => [...prev, msg]);
});
connection.connect();
return () => {
connection.disconnect(); // Cleanup on unmount or roomId change
};
}, [roomId]);
return (
<div>
{messages.map((msg, i) => (
<div key={i}>{msg}</div>
))}
</div>
);
}
Common Effect Patterns:
// Run once on mount
useEffect(() => {
console.log('Component mounted');
}, []); // Empty dependency array
// Run on every render
useEffect(() => {
console.log('Component rendered');
}); // No dependency array
// Run when specific values change
useEffect(() => {
console.log('userId changed:', userId);
}, [userId]); // Dependency array with values
// Cleanup on unmount
useEffect(() => {
const timer = setTimeout(() => {
console.log('Delayed action');
}, 1000);
return () => clearTimeout(timer);
}, []);
Synchronous version of useEffect, runs before browser paint.
Use Cases:
Example:
import { useLayoutEffect, useRef, useState } from 'react';
function Tooltip() {
const ref = useRef(null);
const [tooltipHeight, setTooltipHeight] = useState(0);
useLayoutEffect(() => {
const { height } = ref.current.getBoundingClientRect();
setTooltipHeight(height); // Synchronous update before paint
}, []);
return (
<div ref={ref}>
Tooltip content (height: {tooltipHeight}px)
</div>
);
}
Cache expensive computations between renders.
Syntax:
const cachedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
Example:
import { useMemo } from 'react';
function TodoList({ todos, filter }) {
// Only recompute when todos or filter changes
const visibleTodos = useMemo(() => {
console.log('Filtering todos...');
return todos.filter(todo => {
if (filter === 'all') return true;
if (filter === 'active') return !todo.done;
if (filter === 'completed') return todo.done;
return true;
});
}, [todos, filter]);
return (
<ul>
{visibleTodos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
When to use useMemo:
Anti-pattern (unnecessary):
// ❌ Don't use for simple calculations
const sum = useMemo(() => a + b, [a, b]); // Overkill
// ✅ Just compute directly
const sum = a + b;
Memoize function references between renders.
Syntax:
const cachedFn = useCallback(() => {
// Function logic
}, [dependencies]);
Example:
import { useState, useCallback, memo } from 'react';
const TodoItem = memo(function TodoItem({ todo, onChange, onDelete }) {
console.log('TodoItem rendered:', todo.id);
return (
<li>
<input
type="checkbox"
checked={todo.done}
onChange={() => onChange(todo)}
/>
{todo.text}
<button onClick={() => onDelete(todo.id)}>Delete</button>
</li>
);
});
function TodoList() {
const [todos, setTodos] = useState([]);
// Memoize handlers to prevent TodoItem re-renders
const handleChange = useCallback((todo) => {
setTodos(prev => prev.map(t =>
t.id === todo.id ? { ...t, done: !t.done } : t
));
}, []);
const handleDelete = useCallback((id) => {
setTodos(prev => prev.filter(t => t.id !== id));
}, []);
return (
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onChange={handleChange}
onDelete={handleDelete}
/>
))}
</ul>
);
}
When to use useCallback:
Memoize entire component to prevent unnecessary re-renders.
Syntax:
const MemoizedComponent = memo(function Component(props) {
// Component logic
}, arePropsEqual?);
Example:
import { memo } from 'react';
const ExpensiveComponent = memo(function ExpensiveComponent({ data, onClick }) {
console.log('ExpensiveComponent rendered');
return (
<div>
{data.map(item => (
<div key={item.id} onClick={() => onClick(item)}>
{item.name}
</div>
))}
</div>
);
});
// With custom comparison
const CustomMemoComponent = memo(
function CustomMemoComponent({ user }) {
return <div>{user.name}</div>;
},
(prevProps, nextProps) => {
// Return true if props are equal (skip re-render)
return prevProps.user.id === nextProps.user.id;
}
);
Create mutable reference that persists across renders.
Syntax:
const ref = useRef(initialValue);
DOM Reference Example:
import { useRef } from 'react';
function VideoPlayer({ src, isPlaying }) {
const videoRef = useRef(null);
useEffect(() => {
if (isPlaying) {
videoRef.current.play();
} else {
videoRef.current.pause();
}
}, [isPlaying]);
return <video ref={videoRef} src={src} />;
}
Storing Mutable Values:
function Timer() {
const intervalRef = useRef(null);
const [count, setCount] = useState(0);
function start() {
if (intervalRef.current) return; // Already running
intervalRef.current = setInterval(() => {
setCount(c => c + 1);
}, 1000);
}
function stop() {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
}
return (
<div>
<p>Count: {count}</p>
<button onClick={start}>Start</button>
<button onClick={stop}>Stop</button>
</div>
);
}
Previous Value Pattern:
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
// Usage
function Counter({ count }) {
const prevCount = usePrevious(count);
return (
<div>
<p>Current: {count}</p>
<p>Previous: {prevCount}</p>
</div>
);
}
Mark state updates as non-urgent, keeping UI responsive.
Syntax:
const [isPending, startTransition] = useTransition();
Example:
import { useState, useTransition } from 'react';
function SearchResults() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
function handleChange(e) {
const value = e.target.value;
setQuery(value); // Urgent update (input value)
startTransition(() => {
// Non-urgent update (search results)
const filtered = performExpensiveSearch(value);
setResults(filtered);
});
}
return (
<div>
<input value={query} onChange={handleChange} />
{isPending && <div>Searching...</div>}
<ul>
{results.map(result => (
<li key={result.id}>{result.name}</li>
))}
</ul>
</div>
);
}
Defer updating part of the UI.
Syntax:
const deferredValue = useDeferredValue(value);
Example:
import { useState, useDeferredValue, memo } from 'react';
const SlowList = memo(function SlowList({ items }) {
// Intentionally slow rendering
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
});
function App() {
const [text, setText] = useState('');
const deferredText = useDeferredValue(text);
return (
<>
<input value={text} onChange={e => setText(e.target.value)} />
<SlowList items={filterItems(deferredText)} />
</>
);
}
Manage server action state and pending status.
Syntax:
const [state, formAction, isPending] = useActionState(serverAction, initialState);
Example:
"use client";
import { useActionState } from 'react';
import { updateName } from './actions';
function UpdateNameForm() {
const [state, submitAction, isPending] = useActionState(
updateName,
{ error: null }
);
return (
<form action={submitAction}>
<input type="text" name="name" disabled={isPending} />
{state.error && <span className="error">{state.error}</span>}
<button type="submit" disabled={isPending}>
{isPending ? 'Updating...' : 'Update'}
</button>
</form>
);
}
Server Action (actions.js):
"use server";
export async function updateName(prevState, formData) {
const name = formData.get('name');
if (!name) {
return { error: 'Name is required' };
}
try {
await db.users.updateName(name);
return { error: null };
} catch (err) {
return { error: 'Failed to update name' };
}
}
Custom hooks let you extract and reuse stateful logic across components.
Always prefix custom hooks with use:
useCustomHook // ✅ Correct
customHook // ❌ Wrong
import { useState, useEffect } from 'react';
export function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let ignore = false;
async function fetchData() {
try {
setLoading(true);
setError(null);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (!ignore) {
setData(result);
}
} catch (err) {
if (!ignore) {
setError(err.message);
}
} finally {
if (!ignore) {
setLoading(false);
}
}
}
fetchData();
return () => {
ignore = true;
};
}, [url]);
return { data, loading, error };
}
// Usage
function UserProfile({ userId }) {
const { data: user, loading, error } = useFetch(`/api/users/${userId}`);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>{user.name}</div>;
}
import { useState } from 'react';
export function useFormInput(initialValue = '') {
const [value, setValue] = useState(initialValue);
function handleChange(e) {
setValue(e.target.value);
}
function reset() {
setValue(initialValue);
}
return {
value,
onChange: handleChange,
reset
};
}
// Usage
function LoginForm() {
const email = useFormInput('');
const password = useFormInput('');
function handleSubmit(e) {
e.preventDefault();
console.log('Email:', email.value);
console.log('Password:', password.value);
email.reset();
password.reset();
}
return (
<form onSubmit={handleSubmit}>
<input type="email" {...email} placeholder="Email" />
<input type="password" {...password} placeholder="Password" />
<button type="submit">Login</button>
</form>
);
}
import { useState, useEffect } from 'react';
export function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
useEffect(() => {
try {
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error(error);
}
}, [key, value]);
return [value, setValue];
}
// Usage
function Settings() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
const [language, setLanguage] = useLocalStorage('language', 'en');
return (
<div>
<select value={theme} onChange={e => setTheme(e.target.value)}>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
<select value={language} onChange={e => setLanguage(e.target.value)}>
<option value="en">English</option>
<option value="es">Spanish</option>
</select>
</div>
);
}
import { useState, useEffect } from 'react';
export function useDebounce(value, delay = 500) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
// Usage
function SearchComponent() {
const [searchTerm, setSearchTerm] = useState('');
const debouncedSearchTerm = useDebounce(searchTerm, 500);
useEffect(() => {
if (debouncedSearchTerm) {
// Perform search
console.log('Searching for:', debouncedSearchTerm);
}
}, [debouncedSearchTerm]);
return (
<input
type="text"
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
placeholder="Search..."
/>
);
}
import { useState, useEffect } from 'react';
export function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: undefined,
height: undefined,
});
useEffect(() => {
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
handleResize(); // Set initial size
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return windowSize;
}
// Usage
function ResponsiveComponent() {
const { width, height } = useWindowSize();
return (
<div>
<p>Window width: {width}px</p>
<p>Window height: {height}px</p>
{width < 768 ? <MobileView /> : <DesktopView />}
</div>
);
}
import { useState, useEffect } from 'react';
export function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
function handleOnline() {
setIsOnline(true);
}
function handleOffline() {
setIsOnline(false);
}
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
}
// Usage
function StatusBar() {
const isOnline = useOnlineStatus();
return (
<div className={isOnline ? 'online' : 'offline'}>
{isOnline ? '✅ Online' : '❌ Offline'}
</div>
);
}
Build components that work together while sharing implicit state.
import { createContext, useContext, useState } from 'react';
const TabsContext = createContext();
export function Tabs({ children, defaultValue }) {
const [activeTab, setActiveTab] = useState(defaultValue);
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
<div className="tabs">{children}</div>
</TabsContext.Provider>
);
}
export function TabList({ children }) {
return <div className="tab-list">{children}</div>;
}
export function Tab({ value, children }) {
const { activeTab, setActiveTab } = useContext(TabsContext);
const isActive = activeTab === value;
return (
<button
className={`tab ${isActive ? 'active' : ''}`}
onClick={() => setActiveTab(value)}
>
{children}
</button>
);
}
export function TabPanels({ children }) {
return <div className="tab-panels">{children}</div>;
}
export function TabPanel({ value, children }) {
const { activeTab } = useContext(TabsContext);
if (activeTab !== value) return null;
return <div className="tab-panel">{children}</div>;
}
// Usage
function App() {
return (
<Tabs defaultValue="tab1">
<TabList>
<Tab value="tab1">Tab 1</Tab>
<Tab value="tab2">Tab 2</Tab>
<Tab value="tab3">Tab 3</Tab>
</TabList>
<TabPanels>
<TabPanel value="tab1">Content for Tab 1</TabPanel>
<TabPanel value="tab2">Content for Tab 2</TabPanel>
<TabPanel value="tab3">Content for Tab 3</TabPanel>
</TabPanels>
</Tabs>
);
}
Share code between components using a prop whose value is a function.
function DataFetcher({ url, render }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
});
}, [url]);
return render({ data, loading });
}
// Usage
function App() {
return (
<DataFetcher
url="/api/users"
render={({ data, loading }) => (
loading ? <div>Loading...</div> : (
<ul>
{data?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
)}
/>
);
}
Wrap components to enhance functionality.
import { useEffect, useState } from 'react';
// HOC for data fetching
function withData(Component, url) {
return function WithDataComponent(props) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
});
}, []);
return <Component {...props} data={data} loading={loading} />;
};
}
// Original component
function UserList({ data, loading }) {
if (loading) return <div>Loading...</div>;
return (
<ul>
{data?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
// Enhanced component
const UserListWithData = withData(UserList, '/api/users');
// Usage
function App() {
return <UserListWithData />;
}
Separate logic from presentation.
// Container (logic)
function UserListContainer() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [searchTerm, setSearchTerm] = useState('');
useEffect(() => {
fetchUsers().then(data => {
setUsers(data);
setLoading(false);
});
}, []);
const filteredUsers = users.filter(user =>
user.name.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<UserListPresenter
users={filteredUsers}
loading={loading}
searchTerm={searchTerm}
onSearchChange={setSearchTerm}
/>
);
}
// Presenter (UI)
function UserListPresenter({ users, loading, searchTerm, onSearchChange }) {
if (loading) return <div>Loading...</div>;
return (
<div>
<input
type="text"
value={searchTerm}
onChange={e => onSearchChange(e.target.value)}
placeholder="Search users..."
/>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
Use React DevTools Profiler to identify:
import { memo, useMemo, useCallback } from 'react';
const ExpensiveList = memo(function ExpensiveList({ items, onItemClick }) {
console.log('Rendering ExpensiveList');
return (
<ul>
{items.map(item => (
<li key={item.id} onClick={() => onItemClick(item.id)}>
{item.name}
</li>
))}
</ul>
);
});
function App() {
const [items, setItems] = useState([]);
const [count, setCount] = useState(0);
// Memoize expensive computation
const sortedItems = useMemo(() => {
console.log('Sorting items...');
return [...items].sort((a, b) => a.name.localeCompare(b.name));
}, [items]);
// Memoize callback
const handleItemClick = useCallback((id) => {
console.log('Item clicked:', id);
}, []);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
<ExpensiveList items={sortedItems} onItemClick={handleItemClick} />
</div>
);
}
import { lazy, Suspense } from 'react';
// Lazy load components
const HeavyComponent = lazy(() => import('./HeavyComponent'));
const Dashboard = lazy(() => import('./Dashboard'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}
// Route-based splitting
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
import { FixedSizeList } from 'react-window';
function VirtualizedList({ items }) {
const Row = ({ index, style }) => (
<div style={style}>
{items[index].name}
</div>
);
return (
<FixedSizeList
height={500}
itemCount={items.length}
itemSize={50}
width="100%"
>
{Row}
</FixedSizeList>
);
}
// ❌ Bad - creates new function on every render
function List({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id} onClick={() => console.log(item.id)}>
{item.name}
</li>
))}
</ul>
);
}
// ✅ Good - use useCallback
function List({ items }) {
const handleClick = useCallback((id) => {
console.log(id);
}, []);
return (
<ul>
{items.map(item => (
<li key={item.id} onClick={() => handleClick(item.id)}>
{item.name}
</li>
))}
</ul>
);
}
React Server Components render on the server, reducing client bundle size.
// app/users/page.js (Server Component by default)
async function UsersPage() {
// Fetch data directly on server
const users = await db.users.findAll();
return (
<div>
<h1>Users</h1>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
export default UsersPage;
// components/Counter.js
"use client"; // Mark as client component
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}
// app/page.js (Server Component)
import Counter from '@/components/Counter'; // Client Component
async function HomePage() {
const data = await fetchData(); // Server-side data fetching
return (
<div>
<h1>{data.title}</h1>
<p>{data.description}</p>
<Counter /> {/* Interactive client component */}
</div>
);
}
export default HomePage;
Best for: Component-specific state
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
Best for: Shared state between sibling components
function Parent() {
const [sharedValue, setSharedValue] = useState('');
return (
<>
<ChildA value={sharedValue} onChange={setSharedValue} />
<ChildB value={sharedValue} />
</>
);
}
Best for: App-wide state (theme, auth, language)
const AuthContext = createContext();
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = async (credentials) => {
const user = await api.login(credentials);
setUser(user);
};
const logout = () => setUser(null);
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
Best for: Complex state logic with multiple actions
const StateContext = createContext();
const DispatchContext = createContext();
function reducer(state, action) {
switch (action.type) {
case 'ADD_ITEM':
return { ...state, items: [...state.items, action.payload] };
case 'REMOVE_ITEM':
return { ...state, items: state.items.filter(i => i.id !== action.payload) };
default:
return state;
}
}
function StoreProvider({ children }) {
const [state, dispatch] = useReducer(reducer, { items: [] });
return (
<StateContext.Provider value={state}>
<DispatchContext.Provider value={dispatch}>
{children}
</DispatchContext.Provider>
</StateContext.Provider>
);
}
src/
├── components/
│ ├── common/ # Reusable UI components
│ ├── features/ # Feature-specific components
│ └── layout/ # Layout components
├── hooks/ # Custom hooks
├── context/ # Context providers
├── utils/ # Utility functions
├── services/ # API services
└── types/ # TypeScript types
// ❌ Wrong
const handleAdd = () => {
items.push(newItem);
setItems(items);
};
// ✅ Correct
const handleAdd = () => {
setItems([...items, newItem]);
};
// ❌ Wrong
useEffect(() => {
console.log(userId);
}, []); // Missing userId dependency
// ✅ Correct
useEffect(() => {
console.log(userId);
}, [userId]);
// ❌ Wrong
if (condition) {
const [state, setState] = useState(0);
}
// ✅ Correct
const [state, setState] = useState(0);
if (condition) {
// Use state
}
// ❌ Wrong
function Parent() {
function Child() { // Re-created on every render
return <div>Child</div>;
}
return <Child />;
}
// ✅ Correct
function Child() {
return <div>Child</div>;
}
function Parent() {
return <Child />;
}
Issue: Infinite re-render loop
setState(prev => prev + 1)Issue: Stale closure
Issue: Component not re-rendering
Issue: Memory leak warning
Skill Version : 1.0.0 Last Updated : October 2025 Skill Category : Frontend Development, React, JavaScript Compatible With : React 18+, Next.js 13+, TypeScript
Weekly Installs
63
Repository
GitHub Stars
47
First Seen
Jan 22, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
opencode50
gemini-cli49
codex48
claude-code47
cursor47
github-copilot44
UI组件模式实战指南:构建可复用React组件库与设计系统
10,700 周安装
LobeHub桌面端开发指南:基于Electron的桌面应用架构与功能实现教程
556 周安装
Elasticsearch专家指导:搜索优化、ELK栈部署与分布式系统实战
62 周安装
Render 部署指南:Git 驱动与 Docker 镜像部署全解析
553 周安装
PyTorch深度学习开发专家 | Transformer、扩散模型、LLM开发指南与最佳实践
545 周安装
Rust最佳实践指南:179条规则提升代码质量与性能 | 所有权、错误处理、内存优化
551 周安装
TypeORM 开发指南:TypeScript ORM 框架实战教程与数据库设计
554 周安装