react-development
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReact Development Skill
React开发技能指南
This skill provides comprehensive guidance for building modern React applications using hooks, components, state management, context, effects, and performance optimization techniques based on official React documentation from react.dev.
本技能指南基于react.dev的官方React文档,提供了使用Hooks、组件、状态管理、Context、副作用处理和性能优化技术构建现代React应用的全面指导。
When to Use This Skill
适用场景
Use this skill when:
- Building single-page applications (SPAs) with React
- Creating reusable UI components and component libraries
- Managing complex application state with hooks and context
- Implementing forms, data fetching, and side effects
- Optimizing React application performance
- Building interactive user interfaces with dynamic data
- Migrating class components to functional components with hooks
- Implementing global state management without external libraries
- Creating custom hooks for reusable logic
- Building accessible and performant web applications
在以下场景中使用本技能:
- 使用React构建单页应用(SPA)
- 创建可复用UI组件及组件库
- 使用Hooks和Context管理复杂应用状态
- 实现表单、数据获取与副作用处理
- 优化React应用性能
- 构建包含动态数据的交互式用户界面
- 将类组件迁移为使用Hooks的函数式组件
- 在不依赖外部库的情况下实现全局状态管理
- 创建用于复用逻辑的自定义Hooks
- 构建可访问且高性能的Web应用
Core Concepts
核心概念
Components
组件
Components are the building blocks of React applications. They let you split the UI into independent, reusable pieces.
Functional Components (Modern Approach):
jsx
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// Arrow function syntax
const Greeting = ({ name, age }) => {
return (
<div>
<h2>{name}</h2>
<p>Age: {age}</p>
</div>
);
};Component Composition:
jsx
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}组件是React应用的构建块,可将UI拆分为独立、可复用的部分。
函数式组件(现代方案):
jsx
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// 箭头函数语法
const Greeting = ({ name, age }) => {
return (
<div>
<h2>{name}</h2>
<p>Age: {age}</p>
</div>
);
};组件组合:
jsx
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}JSX
JSX
JSX is a syntax extension for JavaScript that looks similar to HTML. It produces React elements.
JSX Fundamentals:
jsx
// Embedding expressions
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;
// JSX attributes
const image = <img src={user.avatarUrl} alt={user.name} />;
// JSX children
const container = (
<div>
<h1>Welcome</h1>
<p>Get started with React</p>
</div>
);
// Conditional rendering
const greeting = (
<div>
{isLoggedIn ? <UserGreeting /> : <GuestGreeting />}
</div>
);
// Lists and keys
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>{number}</li>
);JSX是JavaScript的语法扩展,外观类似HTML,用于生成React元素。
JSX基础:
jsx
// 嵌入表达式
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;
// JSX属性
const image = <img src={user.avatarUrl} alt={user.name} />;
// JSX子元素
const container = (
<div>
<h1>Welcome</h1>
<p>Get started with React</p>
</div>
);
// 条件渲染
const greeting = (
<div>
{isLoggedIn ? <UserGreeting /> : <GuestGreeting />}
</div>
);
// 列表与键
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>{number}</li>
);Props
Props
Props are arguments passed into React components. They are passed to components via HTML attributes.
Passing and Using Props:
jsx
function Product({ name, price, inStock }) {
return (
<div className="product">
<h3>{name}</h3>
<p>${price}</p>
{inStock ? <span>In Stock</span> : <span>Out of Stock</span>}
</div>
);
}
// Usage
<Product name="Laptop" price={999} inStock={true} />Props with Children:
jsx
function Card({ title, children }) {
return (
<div className="card">
<h2>{title}</h2>
<div className="card-content">
{children}
</div>
</div>
);
}
// Usage
<Card title="Welcome">
<p>This is the card content</p>
<button>Click me</button>
</Card>Default Props:
jsx
function Button({ text = 'Click me', variant = 'primary' }) {
return <button className={variant}>{text}</button>;
}Props是传递给React组件的参数,通过HTML属性的方式传递。
Props的传递与使用:
jsx
function Product({ name, price, inStock }) {
return (
<div className="product">
<h3>{name}</h3>
<p>${price}</p>
{inStock ? <span>In Stock</span> : <span>Out of Stock</span>}
</div>
);
}
// 使用示例
<Product name="Laptop" price={999} inStock={true} />包含子元素的Props:
jsx
function Card({ title, children }) {
return (
<div className="card">
<h2>{title}</h2>
<div className="card-content">
{children}
</div>
</div>
);
}
// 使用示例
<Card title="Welcome">
<p>This is the card content</p>
<button>Click me</button>
</Card>默认Props:
jsx
function Button({ text = 'Click me', variant = 'primary' }) {
return <button className={variant}>{text}</button>;
}State
状态(State)
State is a component's memory. It lets components remember information and respond to user interactions.
Local Component State:
jsx
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(count - 1)}>Decrement</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}状态是组件的内存,允许组件记住信息并响应用户交互。
组件本地状态:
jsx
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(count - 1)}>Decrement</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}React Hooks
React Hooks
Hooks let you use state and other React features in functional components.
Hooks允许你在函数式组件中使用状态及其他React特性。
useState
useState
The useState hook lets you add state to functional components.
Basic Usage:
jsx
import { useState } from 'react';
function Form() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
console.log({ name, email });
};
return (
<form onSubmit={handleSubmit}>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
/>
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
type="email"
/>
<button type="submit">Submit</button>
</form>
);
}State with Objects:
jsx
function UserProfile() {
const [user, setUser] = useState({
name: '',
age: 0,
email: ''
});
const updateField = (field, value) => {
setUser(prev => ({
...prev,
[field]: value
}));
};
return (
<div>
<input
value={user.name}
onChange={(e) => updateField('name', e.target.value)}
/>
<input
type="number"
value={user.age}
onChange={(e) => updateField('age', parseInt(e.target.value))}
/>
<input
type="email"
value={user.email}
onChange={(e) => updateField('email', e.target.value)}
/>
</div>
);
}State with Arrays:
jsx
function TodoList() {
const [todos, setTodos] = useState([]);
const [input, setInput] = useState('');
const addTodo = () => {
setTodos(prev => [...prev, { id: Date.now(), text: input }]);
setInput('');
};
const removeTodo = (id) => {
setTodos(prev => prev.filter(todo => todo.id !== id));
};
return (
<div>
<input value={input} onChange={(e) => setInput(e.target.value)} />
<button onClick={addTodo}>Add</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.text}
<button onClick={() => removeTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
}useState Hook允许你为函数式组件添加状态。
基础用法:
jsx
import { useState } from 'react';
function Form() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
console.log({ name, email });
};
return (
<form onSubmit={handleSubmit}>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
/>
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
type="email"
/>
<button type="submit">Submit</button>
</form>
);
}对象类型状态:
jsx
function UserProfile() {
const [user, setUser] = useState({
name: '',
age: 0,
email: ''
});
const updateField = (field, value) => {
setUser(prev => ({
...prev,
[field]: value
}));
};
return (
<div>
<input
value={user.name}
onChange={(e) => updateField('name', e.target.value)}
/>
<input
type="number"
value={user.age}
onChange={(e) => updateField('age', parseInt(e.target.value))}
/>
<input
type="email"
value={user.email}
onChange={(e) => updateField('email', e.target.value)}
/>
</div>
);
}数组类型状态:
jsx
function TodoList() {
const [todos, setTodos] = useState([]);
const [input, setInput] = useState('');
const addTodo = () => {
setTodos(prev => [...prev, { id: Date.now(), text: input }]);
setInput('');
};
const removeTodo = (id) => {
setTodos(prev => prev.filter(todo => todo.id !== id));
};
return (
<div>
<input value={input} onChange={(e) => setInput(e.target.value)} />
<button onClick={addTodo}>Add</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.text}
<button onClick={() => removeTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
}useEffect
useEffect
The useEffect hook lets you perform side effects in functional components.
Basic Side Effects:
jsx
import { useState, useEffect } from 'react';
function DocumentTitle() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
}Data Fetching:
jsx
function UserData({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
async function fetchUser() {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
if (!cancelled) {
setUser(data);
setError(null);
}
} catch (err) {
if (!cancelled) {
setError(err.message);
}
} finally {
if (!cancelled) {
setLoading(false);
}
}
}
fetchUser();
return () => {
cancelled = true;
};
}, [userId]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>{user?.name}</div>;
}Event Listeners and Cleanup:
jsx
function WindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
function handleResize() {
setSize({
width: window.innerWidth,
height: window.innerHeight
});
}
window.addEventListener('resize', handleResize);
// Cleanup function
return () => {
window.removeEventListener('resize', handleResize);
};
}, []); // Empty dependency array = run once on mount
return <div>{size.width} x {size.height}</div>;
}Timers and Intervals:
jsx
function Timer() {
const [seconds, setSeconds] = useState(0);
const [isRunning, setIsRunning] = useState(false);
useEffect(() => {
if (!isRunning) return;
const interval = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
return () => clearInterval(interval);
}, [isRunning]);
return (
<div>
<p>Seconds: {seconds}</p>
<button onClick={() => setIsRunning(!isRunning)}>
{isRunning ? 'Pause' : 'Start'}
</button>
<button onClick={() => setSeconds(0)}>Reset</button>
</div>
);
}useEffect Hook允许你在函数式组件中执行副作用操作。
基础副作用:
jsx
import { useState, useEffect } from 'react';
function DocumentTitle() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
}数据获取:
jsx
function UserData({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
async function fetchUser() {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
if (!cancelled) {
setUser(data);
setError(null);
}
} catch (err) {
if (!cancelled) {
setError(err.message);
}
} finally {
if (!cancelled) {
setLoading(false);
}
}
}
fetchUser();
return () => {
cancelled = true;
};
}, [userId]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>{user?.name}</div>;
}事件监听与清理:
jsx
function WindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
function handleResize() {
setSize({
width: window.innerWidth,
height: window.innerHeight
});
}
window.addEventListener('resize', handleResize);
// 清理函数
return () => {
window.removeEventListener('resize', handleResize);
};
}, []); // 空依赖数组 = 仅在组件挂载时执行一次
return <div>{size.width} x {size.height}</div>;
}计时器与间隔器:
jsx
function Timer() {
const [seconds, setSeconds] = useState(0);
const [isRunning, setIsRunning] = useState(false);
useEffect(() => {
if (!isRunning) return;
const interval = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
return () => clearInterval(interval);
}, [isRunning]);
return (
<div>
<p>Seconds: {seconds}</p>
<button onClick={() => setIsRunning(!isRunning)}>
{isRunning ? 'Pause' : 'Start'}
</button>
<button onClick={() => setSeconds(0)}>Reset</button>
</div>
);
}useContext
useContext
The useContext hook lets you read and subscribe to context from your component.
Creating and Using Context:
jsx
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext('light');
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={theme}>
<Toolbar />
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
</ThemeContext.Provider>
);
}
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button className={theme}>
I am styled by {theme} theme
</button>
);
}Multiple Contexts:
jsx
const ThemeContext = createContext('light');
const UserContext = createContext(null);
function App() {
const [theme, setTheme] = useState('light');
const [currentUser, setCurrentUser] = useState({ name: 'John', role: 'admin' });
return (
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={currentUser}>
<Dashboard />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
function Dashboard() {
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
return (
<div className={theme}>
<h1>Welcome, {user.name}</h1>
<p>Role: {user.role}</p>
</div>
);
}useContext Hook允许你在组件中读取并订阅Context。
创建与使用Context:
jsx
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext('light');
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={theme}>
<Toolbar />
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
</ThemeContext.Provider>
);
}
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button className={theme}>
I am styled by {theme} theme
</button>
);
}多Context使用:
jsx
const ThemeContext = createContext('light');
const UserContext = createContext(null);
function App() {
const [theme, setTheme] = useState('light');
const [currentUser, setCurrentUser] = useState({ name: 'John', role: 'admin' });
return (
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={currentUser}>
<Dashboard />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
function Dashboard() {
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
return (
<div className={theme}>
<h1>Welcome, {user.name}</h1>
<p>Role: {user.role}</p>
</div>
);
}useReducer
useReducer
The useReducer hook is an alternative to useState for managing complex state logic.
Basic Reducer Pattern:
jsx
import { useReducer } from 'react';
function counterReducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: 0 };
default:
throw new Error(`Unknown action: ${action.type}`);
}
}
function Counter() {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</div>
);
}Complex State Management (Task List Pattern from Context7):
jsx
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 => {
if (t.id === action.task.id) {
return action.task;
} else {
return 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, initialTasks);
function handleAddTask(text) {
dispatch({
type: 'added',
id: nextId++,
text: text,
});
}
function handleChangeTask(task) {
dispatch({
type: 'changed',
task: task
});
}
function handleDeleteTask(taskId) {
dispatch({
type: 'deleted',
id: taskId
});
}
return (
<>
<h1>Prague itinerary</h1>
<AddTask onAddTask={handleAddTask} />
<TaskList
tasks={tasks}
onChangeTask={handleChangeTask}
onDeleteTask={handleDeleteTask}
/>
</>
);
}
let nextId = 3;
const initialTasks = [
{ id: 0, text: 'Visit Kafka Museum', done: true },
{ id: 1, text: 'Watch a puppet show', done: false },
{ id: 2, text: 'Lennon Wall pic', done: false }
];useReducer Hook是useState的替代方案,用于管理复杂状态逻辑。
基础Reducer模式:
jsx
import { useReducer } from 'react';
function counterReducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: 0 };
default:
throw new Error(`Unknown action: ${action.type}`);
}
}
function Counter() {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</div>
);
}复杂状态管理(来自Context7的任务列表模式):
jsx
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 => {
if (t.id === action.task.id) {
return action.task;
} else {
return 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, initialTasks);
function handleAddTask(text) {
dispatch({
type: 'added',
id: nextId++,
text: text,
});
}
function handleChangeTask(task) {
dispatch({
type: 'changed',
task: task
});
}
function handleDeleteTask(taskId) {
dispatch({
type: 'deleted',
id: taskId
});
}
return (
<>
<h1>Prague itinerary</h1>
<AddTask onAddTask={handleAddTask} />
<TaskList
tasks={tasks}
onChangeTask={handleChangeTask}
onDeleteTask={handleDeleteTask}
/>
</>
);
}
let nextId = 3;
const initialTasks = [
{ id: 0, text: 'Visit Kafka Museum', done: true },
{ id: 1, text: 'Watch a puppet show', done: false },
{ id: 2, text: 'Lennon Wall pic', done: false }
];useMemo
useMemo
The useMemo hook lets you cache the result of expensive calculations.
Memoizing Expensive Calculations:
jsx
import { useMemo, useState } from 'react';
function ProductList({ products, category }) {
const [sortOrder, setSortOrder] = useState('asc');
const filteredAndSortedProducts = useMemo(() => {
console.log('Filtering and sorting products...');
const filtered = products.filter(p => p.category === category);
return filtered.sort((a, b) => {
if (sortOrder === 'asc') {
return a.price - b.price;
}
return b.price - a.price;
});
}, [products, category, sortOrder]);
return (
<div>
<button onClick={() => setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc')}>
Sort: {sortOrder}
</button>
<ul>
{filteredAndSortedProducts.map(product => (
<li key={product.id}>{product.name} - ${product.price}</li>
))}
</ul>
</div>
);
}Preventing Object Recreation:
jsx
function SearchResults({ query }) {
const searchOptions = useMemo(() => ({
query,
limit: 10,
caseSensitive: false
}), [query]);
// searchOptions object only recreated when query changes
const results = useSearch(searchOptions);
return <ResultsList results={results} />;
}useMemo Hook允许你缓存昂贵计算的结果。
缓存昂贵计算:
jsx
import { useMemo, useState } from 'react';
function ProductList({ products, category }) {
const [sortOrder, setSortOrder] = useState('asc');
const filteredAndSortedProducts = useMemo(() => {
console.log('Filtering and sorting products...');
const filtered = products.filter(p => p.category === category);
return filtered.sort((a, b) => {
if (sortOrder === 'asc') {
return a.price - b.price;
}
return b.price - a.price;
});
}, [products, category, sortOrder]);
return (
<div>
<button onClick={() => setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc')}>
Sort: {sortOrder}
</button>
<ul>
{filteredAndSortedProducts.map(product => (
<li key={product.id}>{product.name} - ${product.price}</li>
))}
</ul>
</div>
);
}避免对象重复创建:
jsx
function SearchResults({ query }) {
const searchOptions = useMemo(() => ({
query,
limit: 10,
caseSensitive: false
}), [query]);
// 仅当query变化时,searchOptions对象才会重新创建
const results = useSearch(searchOptions);
return <ResultsList results={results} />;
}useCallback
useCallback
The useCallback hook lets you cache a function definition between re-renders.
Memoizing Event Handlers:
jsx
import { useCallback, useState } from 'react';
function ProductPage({ productId }) {
const [items, setItems] = useState([]);
const handleAddToCart = useCallback(() => {
setItems(prevItems => [...prevItems, productId]);
}, [productId]);
return <AddToCartButton onAdd={handleAddToCart} />;
}
// Memoized child component
const AddToCartButton = memo(({ onAdd }) => {
console.log('Button rendered');
return <button onClick={onAdd}>Add to Cart</button>;
});Optimizing Child Components:
jsx
function TodoList() {
const [todos, setTodos] = useState(initialTodos);
const handleToggle = useCallback((id) => {
setTodos(prevTodos =>
prevTodos.map(todo =>
todo.id === id ? { ...todo, done: !todo.done } : todo
)
);
}, []);
const handleDelete = useCallback((id) => {
setTodos(prevTodos => prevTodos.filter(todo => todo.id !== id));
}, []);
return (
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={handleToggle}
onDelete={handleDelete}
/>
))}
</ul>
);
}useCallback Hook允许你在组件重渲染之间缓存函数定义。
缓存事件处理函数:
jsx
import { useCallback, useState } from 'react';
function ProductPage({ productId }) {
const [items, setItems] = useState([]);
const handleAddToCart = useCallback(() => {
setItems(prevItems => [...prevItems, productId]);
}, [productId]);
return <AddToCartButton onAdd={handleAddToCart} />;
}
// 已缓存的子组件
const AddToCartButton = memo(({ onAdd }) => {
console.log('Button rendered');
return <button onClick={onAdd}>Add to Cart</button>;
});优化子组件:
jsx
function TodoList() {
const [todos, setTodos] = useState(initialTodos);
const handleToggle = useCallback((id) => {
setTodos(prevTodos =>
prevTodos.map(todo =>
todo.id === id ? { ...todo, done: !todo.done } : todo
)
);
}, []);
const handleDelete = useCallback((id) => {
setTodos(prevTodos => prevTodos.filter(todo => todo.id !== id));
}, []);
return (
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={handleToggle}
onDelete={handleDelete}
/>
))}
</ul>
);
}useRef
useRef
The useRef hook lets you reference a value that's not needed for rendering.
Accessing DOM Elements:
jsx
import { useRef } from 'react';
function TextInput() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>
<input ref={inputRef} />
<button onClick={handleClick}>Focus input</button>
</>
);
}Storing Mutable Values:
jsx
function Stopwatch() {
const [time, setTime] = useState(0);
const intervalRef = useRef(null);
function handleStart() {
intervalRef.current = setInterval(() => {
setTime(t => t + 1);
}, 10);
}
function handleStop() {
clearInterval(intervalRef.current);
}
return (
<div>
<p>Time: {(time / 100).toFixed(2)}s</p>
<button onClick={handleStart}>Start</button>
<button onClick={handleStop}>Stop</button>
</div>
);
}Video Player Control (Context7 Pattern):
jsx
import { useRef, useState } from 'react';
function VideoPlayer({ src, isPlaying }) {
const ref = useRef(null);
useEffect(() => {
if (isPlaying) {
ref.current.play();
} else {
ref.current.pause();
}
}, [isPlaying]);
return <video ref={ref} src={src} loop playsInline />;
}
function App() {
const [isPlaying, setIsPlaying] = useState(false);
return (
<>
<button onClick={() => setIsPlaying(!isPlaying)}>
{isPlaying ? 'Pause' : 'Play'}
</button>
<VideoPlayer
isPlaying={isPlaying}
src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
/>
</>
);
}useRef Hook允许你引用不需要用于渲染的值。
访问DOM元素:
jsx
import { useRef } from 'react';
function TextInput() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>
<input ref={inputRef} />
<button onClick={handleClick}>Focus input</button>
</>
);
}存储可变值:
jsx
function Stopwatch() {
const [time, setTime] = useState(0);
const intervalRef = useRef(null);
function handleStart() {
intervalRef.current = setInterval(() => {
setTime(t => t + 1);
}, 10);
}
function handleStop() {
clearInterval(intervalRef.current);
}
return (
<div>
<p>Time: {(time / 100).toFixed(2)}s</p>
<button onClick={handleStart}>Start</button>
<button onClick={handleStop}>Stop</button>
</div>
);
}视频播放器控制(Context7模式):
jsx
import { useRef, useState } from 'react';
function VideoPlayer({ src, isPlaying }) {
const ref = useRef(null);
useEffect(() => {
if (isPlaying) {
ref.current.play();
} else {
ref.current.pause();
}
}, [isPlaying]);
return <video ref={ref} src={src} loop playsInline />;
}
function App() {
const [isPlaying, setIsPlaying] = useState(false);
return (
<>
<button onClick={() => setIsPlaying(!isPlaying)}>
{isPlaying ? 'Pause' : 'Play'}
</button>
<VideoPlayer
isPlaying={isPlaying}
src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
/>
</>
);
}State Management Patterns
状态管理模式
Local State Pattern
本地状态模式
Use local state for component-specific data that doesn't need to be shared.
jsx
function LoginForm() {
const [formData, setFormData] = useState({
username: '',
password: '',
rememberMe: false
});
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const handleChange = (field) => (e) => {
const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
setFormData(prev => ({ ...prev, [field]: value }));
};
const validate = () => {
const newErrors = {};
if (!formData.username) newErrors.username = 'Required';
if (!formData.password) newErrors.password = 'Required';
return newErrors;
};
const handleSubmit = async (e) => {
e.preventDefault();
const newErrors = validate();
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
setIsSubmitting(true);
try {
await login(formData);
} catch (error) {
setErrors({ submit: error.message });
} finally {
setIsSubmitting(false);
}
};
return (
<form onSubmit={handleSubmit}>
<input
value={formData.username}
onChange={handleChange('username')}
placeholder="Username"
/>
{errors.username && <span>{errors.username}</span>}
<input
type="password"
value={formData.password}
onChange={handleChange('password')}
placeholder="Password"
/>
{errors.password && <span>{errors.password}</span>}
<label>
<input
type="checkbox"
checked={formData.rememberMe}
onChange={handleChange('rememberMe')}
/>
Remember me
</label>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Logging in...' : 'Log in'}
</button>
{errors.submit && <div>{errors.submit}</div>}
</form>
);
}将本地状态用于组件专属、无需共享的数据。
jsx
function LoginForm() {
const [formData, setFormData] = useState({
username: '',
password: '',
rememberMe: false
});
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const handleChange = (field) => (e) => {
const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
setFormData(prev => ({ ...prev, [field]: value }));
};
const validate = () => {
const newErrors = {};
if (!formData.username) newErrors.username = 'Required';
if (!formData.password) newErrors.password = 'Required';
return newErrors;
};
const handleSubmit = async (e) => {
e.preventDefault();
const newErrors = validate();
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
setIsSubmitting(true);
try {
await login(formData);
} catch (error) {
setErrors({ submit: error.message });
} finally {
setIsSubmitting(false);
}
};
return (
<form onSubmit={handleSubmit}>
<input
value={formData.username}
onChange={handleChange('username')}
placeholder="Username"
/>
{errors.username && <span>{errors.username}</span>}
<input
type="password"
value={formData.password}
onChange={handleChange('password')}
placeholder="Password"
/>
{errors.password && <span>{errors.password}</span>}
<label>
<input
type="checkbox"
checked={formData.rememberMe}
onChange={handleChange('rememberMe')}
/>
Remember me
</label>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Logging in...' : 'Log in'}
</button>
{errors.submit && <div>{errors.submit}</div>}
</form>
);
}Context API Pattern
Context API模式
Use Context for global or widely-shared state like themes, user authentication, or preferences.
Theme Context with Provider:
jsx
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext(null);
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prevTheme => prevTheme === '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 App() {
return (
<ThemeProvider>
<Page />
</ThemeProvider>
);
}
function Page() {
const { theme, toggleTheme } = useTheme();
return (
<div className={`page-${theme}`}>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
}将Context用于全局或广泛共享的状态,如主题、用户认证或偏好设置。
带Provider的主题Context:
jsx
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext(null);
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prevTheme => prevTheme === '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 App() {
return (
<ThemeProvider>
<Page />
</ThemeProvider>
);
}
function Page() {
const { theme, toggleTheme } = useTheme();
return (
<div className={`page-${theme}`}>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
}Reducer + Context Pattern (Context7 Best Practice)
Reducer + Context模式(Context7最佳实践)
Combine useReducer with Context for scalable state management.
Task Management with Reducer + Context:
jsx
import { createContext, useContext, useReducer } from 'react';
// Context for tasks data
const TasksContext = createContext(null);
// Context for dispatch function
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 tasksReducer(tasks, action) {
switch (action.type) {
case 'added': {
return [...tasks, {
id: action.id,
text: action.text,
done: false
}];
}
case 'changed': {
return tasks.map(t => {
if (t.id === action.task.id) {
return action.task;
}
return t;
});
}
case 'deleted': {
return tasks.filter(t => t.id !== action.id);
}
default: {
throw Error('Unknown action: ' + action.type);
}
}
}
const initialTasks = [
{ id: 0, text: 'Philosopher's Path', done: true },
{ id: 1, text: 'Visit the temple', done: false },
{ id: 2, text: 'Drink matcha', done: false }
];
// Component using the pattern
function AddTask() {
const [text, setText] = useState('');
const dispatch = useTasksDispatch();
return (
<>
<input
placeholder="Add task"
value={text}
onChange={e => setText(e.target.value)}
/>
<button onClick={() => {
setText('');
dispatch({
type: 'added',
id: nextId++,
text: text,
});
}}>Add</button>
</>
);
}
function TaskList() {
const tasks = useTasks();
return (
<ul>
{tasks.map(task => (
<Task key={task.id} task={task} />
))}
</ul>
);
}
function Task({ task }) {
const [isEditing, setIsEditing] = useState(false);
const dispatch = useTasksDispatch();
let taskContent;
if (isEditing) {
taskContent = (
<>
<input
value={task.text}
onChange={e => {
dispatch({
type: 'changed',
task: {
...task,
text: e.target.value
}
});
}} />
<button onClick={() => setIsEditing(false)}>
Save
</button>
</>
);
} else {
taskContent = (
<>
{task.text}
<button onClick={() => setIsEditing(true)}>
Edit
</button>
</>
);
}
return (
<label>
<input
type="checkbox"
checked={task.done}
onChange={e => {
dispatch({
type: 'changed',
task: {
...task,
done: e.target.checked
}
});
}}
/>
{taskContent}
<button onClick={() => {
dispatch({
type: 'deleted',
id: task.id
});
}}>
Delete
</button>
</label>
);
}
let nextId = 3;结合useReducer与Context实现可扩展的状态管理。
使用Reducer + Context的任务管理:
jsx
import { createContext, useContext, useReducer } from 'react';
// 任务数据的Context
const TasksContext = createContext(null);
// 分发函数的Context
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 tasksReducer(tasks, action) {
switch (action.type) {
case 'added': {
return [...tasks, {
id: action.id,
text: action.text,
done: false
}];
}
case 'changed': {
return tasks.map(t => {
if (t.id === action.task.id) {
return action.task;
}
return t;
});
}
case 'deleted': {
return tasks.filter(t => t.id !== action.id);
}
default: {
throw Error('Unknown action: ' + action.type);
}
}
}
const initialTasks = [
{ id: 0, text: 'Philosopher's Path', done: true },
{ id: 1, text: 'Visit the temple', done: false },
{ id: 2, text: 'Drink matcha', done: false }
];
// 使用该模式的组件
function AddTask() {
const [text, setText] = useState('');
const dispatch = useTasksDispatch();
return (
<>
<input
placeholder="Add task"
value={text}
onChange={e => setText(e.target.value)}
/>
<button onClick={() => {
setText('');
dispatch({
type: 'added',
id: nextId++,
text: text,
});
}}>Add</button>
</>
);
}
function TaskList() {
const tasks = useTasks();
return (
<ul>
{tasks.map(task => (
<Task key={task.id} task={task} />
))}
</ul>
);
}
function Task({ task }) {
const [isEditing, setIsEditing] = useState(false);
const dispatch = useTasksDispatch();
let taskContent;
if (isEditing) {
taskContent = (
<>
<input
value={task.text}
onChange={e => {
dispatch({
type: 'changed',
task: {
...task,
text: e.target.value
}
});
}} />
<button onClick={() => setIsEditing(false)}>
Save
</button>
</>
);
} else {
taskContent = (
<>
{task.text}
<button onClick={() => setIsEditing(true)}>
Edit
</button>
</>
);
}
return (
<label>
<input
type="checkbox"
checked={task.done}
onChange={e => {
dispatch({
type: 'changed',
task: {
...task,
done: e.target.checked
}
});
}}
/>
{taskContent}
<button onClick={() => {
dispatch({
type: 'deleted',
id: task.id
});
}}>
Delete
</button>
</label>
);
}
let nextId = 3;Custom Hooks
自定义Hooks
Custom hooks let you extract component logic into reusable functions.
自定义Hooks允许你将组件逻辑提取为可复用的函数。
Basic Custom Hook
基础自定义Hook
jsx
import { useState, useEffect } from 'react';
function useWindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
function handleResize() {
setSize({
width: window.innerWidth,
height: window.innerHeight
});
}
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return size;
}
// Usage
function Component() {
const { width, height } = useWindowSize();
return <div>{width} x {height}</div>;
}jsx
import { useState, useEffect } from 'react';
function useWindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
function handleResize() {
setSize({
width: window.innerWidth,
height: window.innerHeight
});
}
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return size;
}
// 使用示例
function Component() {
const { width, height } = useWindowSize();
return <div>{width} x {height}</div>;
}Online Status Hook (Context7 Pattern)
在线状态Hook(Context7模式)
jsx
import { useState, useEffect } from 'react';
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(true);
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 <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;
}jsx
import { useState, useEffect } from 'react';
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(true);
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 <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;
}Form Hook
表单Hook
jsx
function useForm(initialValues, onSubmit) {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const handleChange = (name, value) => {
setValues(prev => ({ ...prev, [name]: value }));
// Clear error when user starts typing
if (errors[name]) {
setErrors(prev => ({ ...prev, [name]: null }));
}
};
const handleSubmit = async (e) => {
e.preventDefault();
setIsSubmitting(true);
try {
await onSubmit(values);
} catch (error) {
setErrors({ submit: error.message });
} finally {
setIsSubmitting(false);
}
};
const reset = () => {
setValues(initialValues);
setErrors({});
};
return {
values,
errors,
isSubmitting,
handleChange,
handleSubmit,
setErrors,
reset
};
}
// Usage
function ContactForm() {
const { values, errors, isSubmitting, handleChange, handleSubmit } = useForm(
{ name: '', email: '', message: '' },
async (data) => {
await fetch('/api/contact', {
method: 'POST',
body: JSON.stringify(data)
});
}
);
return (
<form onSubmit={handleSubmit}>
<input
value={values.name}
onChange={(e) => handleChange('name', e.target.value)}
/>
{errors.name && <span>{errors.name}</span>}
<input
value={values.email}
onChange={(e) => handleChange('email', e.target.value)}
/>
{errors.email && <span>{errors.email}</span>}
<textarea
value={values.message}
onChange={(e) => handleChange('message', e.target.value)}
/>
{errors.message && <span>{errors.message}</span>}
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Sending...' : 'Send'}
</button>
</form>
);
}jsx
function useForm(initialValues, onSubmit) {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const handleChange = (name, value) => {
setValues(prev => ({ ...prev, [name]: value }));
// 用户开始输入时清除错误
if (errors[name]) {
setErrors(prev => ({ ...prev, [name]: null }));
}
};
const handleSubmit = async (e) => {
e.preventDefault();
setIsSubmitting(true);
try {
await onSubmit(values);
} catch (error) {
setErrors({ submit: error.message });
} finally {
setIsSubmitting(false);
}
};
const reset = () => {
setValues(initialValues);
setErrors({});
};
return {
values,
errors,
isSubmitting,
handleChange,
handleSubmit,
setErrors,
reset
};
}
// 使用示例
function ContactForm() {
const { values, errors, isSubmitting, handleChange, handleSubmit } = useForm(
{ name: '', email: '', message: '' },
async (data) => {
await fetch('/api/contact', {
method: 'POST',
body: JSON.stringify(data)
});
}
);
return (
<form onSubmit={handleSubmit}>
<input
value={values.name}
onChange={(e) => handleChange('name', e.target.value)}
/>
{errors.name && <span>{errors.name}</span>}
<input
value={values.email}
onChange={(e) => handleChange('email', e.target.value)}
/>
{errors.email && <span>{errors.email}</span>}
<textarea
value={values.message}
onChange={(e) => handleChange('message', e.target.value)}
/>
{errors.message && <span>{errors.message}</span>}
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Sending...' : 'Send'}
</button>
</form>
);
}Fetch Hook
数据获取Hook
jsx
function useFetch(url, options = {}) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
async function fetchData() {
try {
setLoading(true);
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (!cancelled) {
setData(result);
setError(null);
}
} catch (err) {
if (!cancelled) {
setError(err.message);
setData(null);
}
} finally {
if (!cancelled) {
setLoading(false);
}
}
}
fetchData();
return () => {
cancelled = true;
};
}, [url, JSON.stringify(options)]);
return { data, loading, error };
}
// Usage
function UserProfile({ userId }) {
const { data, loading, error } = useFetch(`/api/users/${userId}`);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>{data.name}</div>;
}jsx
function useFetch(url, options = {}) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
async function fetchData() {
try {
setLoading(true);
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (!cancelled) {
setData(result);
setError(null);
}
} catch (err) {
if (!cancelled) {
setError(err.message);
setData(null);
}
} finally {
if (!cancelled) {
setLoading(false);
}
}
}
fetchData();
return () => {
cancelled = true;
};
}, [url, JSON.stringify(options)]);
return { data, loading, error };
}
// 使用示例
function UserProfile({ userId }) {
const { data, loading, error } = useFetch(`/api/users/${userId}`);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>{data.name}</div>;
}Local Storage Hook
本地存储Hook
jsx
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
const setValue = (value) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(error);
}
};
return [storedValue, setValue];
}
// Usage
function Settings() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
const [fontSize, setFontSize] = useLocalStorage('fontSize', 16);
return (
<div>
<select value={theme} onChange={(e) => setTheme(e.target.value)}>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
<input
type="number"
value={fontSize}
onChange={(e) => setFontSize(parseInt(e.target.value))}
/>
</div>
);
}jsx
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
const setValue = (value) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(error);
}
};
return [storedValue, setValue];
}
// 使用示例
function Settings() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
const [fontSize, setFontSize] = useLocalStorage('fontSize', 16);
return (
<div>
<select value={theme} onChange={(e) => setTheme(e.target.value)}>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
<input
type="number"
value={fontSize}
onChange={(e) => setFontSize(parseInt(e.target.value))}
/>
</div>
);
}Performance Optimization
性能优化
React.memo
React.memo
Memoize components to prevent unnecessary re-renders.
jsx
import { memo } from 'react';
const ExpensiveComponent = memo(function ExpensiveComponent({ data, onAction }) {
console.log('Rendering expensive component');
return (
<div>
{data.map(item => (
<div key={item.id}>{item.name}</div>
))}
<button onClick={onAction}>Action</button>
</div>
);
});
// With custom comparison
const CustomMemoComponent = memo(
function Component({ user }) {
return <div>{user.name}</div>;
},
(prevProps, nextProps) => {
// Return true if props are equal (skip re-render)
return prevProps.user.id === nextProps.user.id;
}
);对组件进行缓存,避免不必要的重渲染。
jsx
import { memo } from 'react';
const ExpensiveComponent = memo(function ExpensiveComponent({ data, onAction }) {
console.log('Rendering expensive component');
return (
<div>
{data.map(item => (
<div key={item.id}>{item.name}</div>
))}
<button onClick={onAction}>Action</button>
</div>
);
});
// 自定义比较逻辑
const CustomMemoComponent = memo(
function Component({ user }) {
return <div>{user.name}</div>;
},
(prevProps, nextProps) => {
// 如果props相等则返回true(跳过重渲染)
return prevProps.user.id === nextProps.user.id;
}
);Lazy Loading
懒加载
Load components on demand to reduce initial bundle size.
jsx
import { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
const AdminPanel = lazy(() => import('./AdminPanel'));
function App() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
</div>
);
}
// Lazy loading with routes
function Dashboard() {
const [showAdmin, setShowAdmin] = useState(false);
return (
<div>
<button onClick={() => setShowAdmin(true)}>
Show Admin Panel
</button>
{showAdmin && (
<Suspense fallback={<Spinner />}>
<AdminPanel />
</Suspense>
)}
</div>
);
}按需加载组件以减小初始包体积。
jsx
import { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
const AdminPanel = lazy(() => import('./AdminPanel'));
function App() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
</div>
);
}
// 结合路由的懒加载
function Dashboard() {
const [showAdmin, setShowAdmin] = useState(false);
return (
<div>
<button onClick={() => setShowAdmin(true)}>
Show Admin Panel
</button>
{showAdmin && (
<Suspense fallback={<Spinner />}>
<AdminPanel />
</Suspense>
)}
</div>
);
}Code Splitting
代码分割
Split your code into smaller chunks for better performance.
jsx
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading page...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}将代码拆分为更小的块以提升性能。
jsx
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading page...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}Virtual Scrolling
虚拟滚动
Render only visible items in large lists.
jsx
function VirtualList({ items, height, itemHeight }) {
const [scrollTop, setScrollTop] = useState(0);
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(
startIndex + Math.ceil(height / itemHeight),
items.length
);
const visibleItems = items.slice(startIndex, endIndex);
const offsetY = startIndex * itemHeight;
const totalHeight = items.length * itemHeight;
return (
<div
style={{ height, overflow: 'auto' }}
onScroll={(e) => setScrollTop(e.target.scrollTop)}
>
<div style={{ height: totalHeight, position: 'relative' }}>
<div style={{ transform: `translateY(${offsetY}px)` }}>
{visibleItems.map((item, index) => (
<div
key={startIndex + index}
style={{ height: itemHeight }}
>
{item}
</div>
))}
</div>
</div>
</div>
);
}仅渲染大型列表中可见的项。
jsx
function VirtualList({ items, height, itemHeight }) {
const [scrollTop, setScrollTop] = useState(0);
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(
startIndex + Math.ceil(height / itemHeight),
items.length
);
const visibleItems = items.slice(startIndex, endIndex);
const offsetY = startIndex * itemHeight;
const totalHeight = items.length * itemHeight;
return (
<div
style={{ height, overflow: 'auto' }}
onScroll={(e) => setScrollTop(e.target.scrollTop)}
>
<div style={{ height: totalHeight, position: 'relative' }}>
<div style={{ transform: `translateY(${offsetY}px)` }}>
{visibleItems.map((item, index) => (
<div
key={startIndex + index}
style={{ height: itemHeight }}
>
{item}
</div>
))}
</div>
</div>
</div>
);
}Best Practices from Context7 Research
Context7研究中的最佳实践
1. Proper Dependency Arrays
1. 正确的依赖数组
Always include all dependencies in useEffect, useMemo, and useCallback.
jsx
// ❌ Bad - missing dependencies
useEffect(() => {
fetchData(userId);
}, []);
// ✅ Good - all dependencies included
useEffect(() => {
fetchData(userId);
}, [userId]);始终在useEffect、useMemo和useCallback中包含所有依赖项。
jsx
// ❌ 错误 - 缺少依赖项
useEffect(() => {
fetchData(userId);
}, []);
// ✅ 正确 - 包含所有依赖项
useEffect(() => {
fetchData(userId);
}, [userId]);2. Cleanup Functions
2. 清理函数
Always cleanup side effects to prevent memory leaks.
jsx
useEffect(() => {
const subscription = api.subscribe(id);
return () => {
subscription.unsubscribe();
};
}, [id]);始终清理副作用以防止内存泄漏。
jsx
useEffect(() => {
const subscription = api.subscribe(id);
return () => {
subscription.unsubscribe();
};
}, [id]);3. Separate Concerns
3. 关注点分离
Split context for data and dispatch to optimize re-renders.
jsx
// ✅ Good - separate contexts
const TasksContext = createContext(null);
const TasksDispatchContext = createContext(null);
// Components that only dispatch won't re-render when tasks change
function AddTask() {
const dispatch = useTasksDispatch(); // No re-render when tasks change
// ...
}拆分数据与分发的Context以优化重渲染。
jsx
// ✅ 正确 - 分离Context
const TasksContext = createContext(null);
const TasksDispatchContext = createContext(null);
// 仅使用分发函数的组件不会在任务变化时重渲染
function AddTask() {
const dispatch = useTasksDispatch(); // 任务变化时不会重渲染
// ...
}4. Avoid Inline Object Creation
4. 避免内联对象创建
Use useMemo or useCallback to prevent unnecessary re-renders.
jsx
// ❌ Bad - new object on every render
<Component style={{ margin: 10 }} />
// ✅ Good - memoized object
const style = useMemo(() => ({ margin: 10 }), []);
<Component style={style} />使用useMemo或useCallback避免不必要的重渲染。
jsx
// ❌ 错误 - 每次渲染都创建新对象
<Component style={{ margin: 10 }} />
// ✅ 正确 - 已缓存的对象
const style = useMemo(() => ({ margin: 10 }), []);
<Component style={style} />5. State Updater Functions
5. 状态更新器函数
Use updater functions when new state depends on previous state.
jsx
// ❌ Bad - may use stale state
setCount(count + 1);
// ✅ Good - uses current state
setCount(prevCount => prevCount + 1);当新状态依赖于先前状态时,使用更新器函数。
jsx
// ❌ 错误 - 可能使用过期状态
setCount(count + 1);
// ✅ 正确 - 使用当前状态
setCount(prevCount => prevCount + 1);6. Extract Complex Logic
6. 提取复杂逻辑
Move complex state logic to reducers or custom hooks.
jsx
// ✅ Good - complex logic in reducer
function cartReducer(state, action) {
switch (action.type) {
case 'add_item':
// Complex logic here
return newState;
case 'remove_item':
// Complex logic here
return newState;
default:
return state;
}
}将复杂状态逻辑移至reducer或自定义Hooks中。
jsx
// ✅ 正确 - 复杂逻辑在reducer中
function cartReducer(state, action) {
switch (action.type) {
case 'add_item':
// 复杂逻辑在此处
return newState;
case 'remove_item':
// 复杂逻辑在此处
return newState;
default:
return state;
}
}7. Key Props for Lists
7. 列表的Key属性
Always provide unique keys for list items.
jsx
// ❌ Bad - using index as key
{items.map((item, index) => <div key={index}>{item}</div>)}
// ✅ Good - using unique ID
{items.map(item => <div key={item.id}>{item.text}</div>)}始终为列表项提供唯一的key。
jsx
// ❌ 错误 - 使用索引作为key
{items.map((item, index) => <div key={index}>{item}</div>)}
// ✅ 正确 - 使用唯一ID
{items.map(item => <div key={item.id}>{item.text}</div>)}8. Controlled Components
8. 受控组件
Prefer controlled components for form inputs.
jsx
function Form() {
const [value, setValue] = useState('');
return (
<input
value={value}
onChange={(e) => setValue(e.target.value)}
/>
);
}优先为表单输入使用受控组件。
jsx
function Form() {
const [value, setValue] = useState('');
return (
<input
value={value}
onChange={(e) => setValue(e.target.value)}
/>
);
}9. Error Boundaries
9. 错误边界
Implement error boundaries to catch and handle errors gracefully.
jsx
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
console.error('Error caught:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
// Usage
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>实现错误边界以优雅地捕获和处理错误。
jsx
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
console.error('Error caught:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
// 使用示例
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>10. Prop Types or TypeScript
10. Prop Types或TypeScript
Use PropTypes or TypeScript for type checking.
jsx
import PropTypes from 'prop-types';
function User({ name, age, email }) {
return <div>{name} ({age})</div>;
}
User.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number.isRequired,
email: PropTypes.string
};
User.defaultProps = {
email: 'no-email@example.com'
};使用PropTypes或TypeScript进行类型检查。
jsx
import PropTypes from 'prop-types';
function User({ name, age, email }) {
return <div>{name} ({age})</div>;
}
User.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number.isRequired,
email: PropTypes.string
};
User.defaultProps = {
email: 'no-email@example.com'
};Additional Examples
额外示例
Example 1: Multi-Step Form
示例1:多步骤表单
jsx
function MultiStepForm() {
const [step, setStep] = useState(1);
const [formData, setFormData] = useState({
personalInfo: {},
address: {},
preferences: {}
});
const updateFormData = (section, data) => {
setFormData(prev => ({
...prev,
[section]: { ...prev[section], ...data }
}));
};
const nextStep = () => setStep(s => s + 1);
const prevStep = () => setStep(s => s - 1);
return (
<div>
{step === 1 && (
<PersonalInfoStep
data={formData.personalInfo}
onNext={(data) => {
updateFormData('personalInfo', data);
nextStep();
}}
/>
)}
{step === 2 && (
<AddressStep
data={formData.address}
onNext={(data) => {
updateFormData('address', data);
nextStep();
}}
onPrev={prevStep}
/>
)}
{step === 3 && (
<PreferencesStep
data={formData.preferences}
onSubmit={(data) => {
updateFormData('preferences', data);
submitForm({ ...formData, preferences: data });
}}
onPrev={prevStep}
/>
)}
</div>
);
}jsx
function MultiStepForm() {
const [step, setStep] = useState(1);
const [formData, setFormData] = useState({
personalInfo: {},
address: {},
preferences: {}
});
const updateFormData = (section, data) => {
setFormData(prev => ({
...prev,
[section]: { ...prev[section], ...data }
}));
};
const nextStep = () => setStep(s => s + 1);
const prevStep = () => setStep(s => s - 1);
return (
<div>
{step === 1 && (
<PersonalInfoStep
data={formData.personalInfo}
onNext={(data) => {
updateFormData('personalInfo', data);
nextStep();
}}
/>
)}
{step === 2 && (
<AddressStep
data={formData.address}
onNext={(data) => {
updateFormData('address', data);
nextStep();
}}
onPrev={prevStep}
/>
)}
{step === 3 && (
<PreferencesStep
data={formData.preferences}
onSubmit={(data) => {
updateFormData('preferences', data);
submitForm({ ...formData, preferences: data });
}}
onPrev={prevStep}
/>
)}
</div>
);
}Example 2: Infinite Scroll
示例2:无限滚动
jsx
function InfiniteScroll() {
const [items, setItems] = useState([]);
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const loadMore = useCallback(async () => {
if (loading || !hasMore) return;
setLoading(true);
try {
const newItems = await fetchItems(page);
setItems(prev => [...prev, ...newItems]);
setHasMore(newItems.length > 0);
setPage(p => p + 1);
} finally {
setLoading(false);
}
}, [page, loading, hasMore]);
useEffect(() => {
const handleScroll = () => {
if (
window.innerHeight + window.scrollY >=
document.documentElement.scrollHeight - 500
) {
loadMore();
}
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, [loadMore]);
return (
<div>
{items.map(item => <Item key={item.id} data={item} />)}
{loading && <div>Loading...</div>}
{!hasMore && <div>No more items</div>}
</div>
);
}jsx
function InfiniteScroll() {
const [items, setItems] = useState([]);
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const loadMore = useCallback(async () => {
if (loading || !hasMore) return;
setLoading(true);
try {
const newItems = await fetchItems(page);
setItems(prev => [...prev, ...newItems]);
setHasMore(newItems.length > 0);
setPage(p => p + 1);
} finally {
setLoading(false);
}
}, [page, loading, hasMore]);
useEffect(() => {
const handleScroll = () => {
if (
window.innerHeight + window.scrollY >=
document.documentElement.scrollHeight - 500
) {
loadMore();
}
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, [loadMore]);
return (
<div>
{items.map(item => <Item key={item.id} data={item} />)}
{loading && <div>Loading...</div>}
{!hasMore && <div>No more items</div>}
</div>
);
}Example 3: Debounced Search
示例3:防抖搜索
jsx
function useDebounce(value, delay) {
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);
const [results, setResults] = useState([]);
useEffect(() => {
if (debouncedSearchTerm) {
searchAPI(debouncedSearchTerm).then(setResults);
} else {
setResults([]);
}
}, [debouncedSearchTerm]);
return (
<div>
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search..."
/>
<ul>
{results.map(result => (
<li key={result.id}>{result.title}</li>
))}
</ul>
</div>
);
}jsx
function useDebounce(value, delay) {
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);
const [results, setResults] = useState([]);
useEffect(() => {
if (debouncedSearchTerm) {
searchAPI(debouncedSearchTerm).then(setResults);
} else {
setResults([]);
}
}, [debouncedSearchTerm]);
return (
<div>
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search..."
/>
<ul>
{results.map(result => (
<li key={result.id}>{result.title}</li>
))}
</ul>
</div>
);
}Example 4: Modal with Portal
示例4:使用Portal的模态框
jsx
import { createPortal } from 'react-dom';
function Modal({ isOpen, onClose, children }) {
if (!isOpen) return null;
return createPortal(
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<button className="close-button" onClick={onClose}>×</button>
{children}
</div>
</div>,
document.body
);
}
// Usage
function App() {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<div>
<button onClick={() => setIsModalOpen(true)}>Open Modal</button>
<Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)}>
<h2>Modal Content</h2>
<p>This is a modal dialog</p>
</Modal>
</div>
);
}jsx
import { createPortal } from 'react-dom';
function Modal({ isOpen, onClose, children }) {
if (!isOpen) return null;
return createPortal(
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<button className="close-button" onClick={onClose}>×</button>
{children}
</div>
</div>,
document.body
);
}
// 使用示例
function App() {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<div>
<button onClick={() => setIsModalOpen(true)}>Open Modal</button>
<Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)}>
<h2>Modal Content</h2>
<p>This is a modal dialog</p>
</Modal>
</div>
);
}Example 5: Drag and Drop
示例5:拖拽功能
jsx
function DragDropList() {
const [items, setItems] = useState([
{ id: 1, text: 'Item 1' },
{ id: 2, text: 'Item 2' },
{ id: 3, text: 'Item 3' }
]);
const [draggedItem, setDraggedItem] = useState(null);
const handleDragStart = (item) => {
setDraggedItem(item);
};
const handleDragOver = (e) => {
e.preventDefault();
};
const handleDrop = (targetItem) => {
if (!draggedItem || draggedItem.id === targetItem.id) return;
const draggedIndex = items.findIndex(i => i.id === draggedItem.id);
const targetIndex = items.findIndex(i => i.id === targetItem.id);
const newItems = [...items];
newItems.splice(draggedIndex, 1);
newItems.splice(targetIndex, 0, draggedItem);
setItems(newItems);
setDraggedItem(null);
};
return (
<ul>
{items.map(item => (
<li
key={item.id}
draggable
onDragStart={() => handleDragStart(item)}
onDragOver={handleDragOver}
onDrop={() => handleDrop(item)}
>
{item.text}
</li>
))}
</ul>
);
}jsx
function DragDropList() {
const [items, setItems] = useState([
{ id: 1, text: 'Item 1' },
{ id: 2, text: 'Item 2' },
{ id: 3, text: 'Item 3' }
]);
const [draggedItem, setDraggedItem] = useState(null);
const handleDragStart = (item) => {
setDraggedItem(item);
};
const handleDragOver = (e) => {
e.preventDefault();
};
const handleDrop = (targetItem) => {
if (!draggedItem || draggedItem.id === targetItem.id) return;
const draggedIndex = items.findIndex(i => i.id === draggedItem.id);
const targetIndex = items.findIndex(i => i.id === targetItem.id);
const newItems = [...items];
newItems.splice(draggedIndex, 1);
newItems.splice(targetIndex, 0, draggedItem);
setItems(newItems);
setDraggedItem(null);
};
return (
<ul>
{items.map(item => (
<li
key={item.id}
draggable
onDragStart={() => handleDragStart(item)}
onDragOver={handleDragOver}
onDrop={() => handleDrop(item)}
>
{item.text}
</li>
))}
</ul>
);
}Summary
总结
This React development skill covers:
- Core Concepts: Components, JSX, Props, State
- Essential Hooks: useState, useEffect, useContext, useReducer, useMemo, useCallback, useRef
- State Management: Local state, Context API, Reducer + Context pattern
- Custom Hooks: Reusable logic extraction patterns
- Performance: Memoization, lazy loading, code splitting, virtual scrolling
- Best Practices: From Context7 research including proper dependencies, cleanup, separation of concerns
- Real-world Examples: Forms, infinite scroll, search, modals, drag-and-drop
The patterns and examples are based on official React documentation (Trust Score: 10) and represent modern React development practices focusing on functional components and hooks.
本React开发技能指南涵盖:
- 核心概念:组件、JSX、Props、状态
- 必备Hooks:useState、useEffect、useContext、useReducer、useMemo、useCallback、useRef
- 状态管理:本地状态、Context API、Reducer + Context模式
- 自定义Hooks:可复用逻辑提取模式
- 性能优化:缓存、懒加载、代码分割、虚拟滚动
- 最佳实践:来自Context7研究的内容,包括正确的依赖项、清理、关注点分离
- 实战示例:表单、无限滚动、搜索、模态框、拖拽
所有模式与示例均基于React官方文档(可信度评分:10),代表了以函数式组件和Hooks为核心的现代React开发实践。