react-patterns

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React Patterns - Modern Development Guide

React模式 - 现代开发指南

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.
一份全面掌握现代React开发模式的指南,包括React 18+特性、Hooks、组件组合、状态管理策略以及性能优化技术,助力构建可扩展的Web应用。

When to Use This Skill

何时使用本技能

Use this skill when:
  • Building modern React applications with functional components and hooks
  • Implementing complex state management with useReducer and Context API
  • Optimizing React application performance with memoization techniques
  • Creating reusable custom hooks for shared logic
  • Working with Server Components and React Server Components (RSC)
  • Managing forms, side effects, and asynchronous operations
  • Refactoring class components to modern functional patterns
  • Building type-safe React applications with proper patterns
  • Implementing advanced component composition patterns
  • Debugging React performance issues and unnecessary re-renders
在以下场景使用本技能:
  • 使用函数式组件和Hooks构建现代React应用
  • 结合useReducer和Context API实现复杂状态管理
  • 使用记忆化技术优化React应用性能
  • 创建可复用的自定义Hooks以共享逻辑
  • 开发Server Components和React Server Components(RSC)
  • 处理表单、副作用和异步操作
  • 将类组件重构为现代函数式模式
  • 遵循规范模式构建类型安全的React应用
  • 实现高级组件组合模式
  • 调试React性能问题和不必要的重渲染

Core Concepts

核心概念

React Philosophy

React设计理念

Modern React emphasizes:
  • Functional Components: Pure functions that return JSX
  • Hooks: Composable state and side effect management
  • Declarative UI: Describe what the UI should look like, React handles updates
  • Component Composition: Build complex UIs from simple, reusable components
  • Unidirectional Data Flow: Props flow down, events flow up
  • Immutability: Never mutate state directly, always create new objects/arrays
现代React强调:
  • 函数式组件:返回JSX的纯函数
  • Hooks:可组合的状态和副作用管理
  • 声明式UI:描述UI应该呈现的样子,React负责处理更新
  • 组件组合:从简单、可复用的组件构建复杂UI
  • 单向数据流:Props向下传递,事件向上传递
  • 不可变性:永远不要直接修改状态,始终创建新的对象/数组

Component Types

组件类型

  1. Presentational Components: Focus on UI, receive data via props
  2. Container Components: Handle logic, state, and side effects
  3. Server Components: Render on the server, no client JavaScript
  4. Client Components: Interactive components marked with
    "use client"
  5. Async Components: Server components that can await data
  1. 展示型组件:专注于UI,通过Props接收数据
  2. 容器型组件:处理逻辑、状态和副作用
  3. Server组件:在服务器端渲染,不包含客户端JavaScript
  4. Client组件:标记有
    "use client"
    的交互式组件
  5. 异步组件:可以等待数据的Server组件

React Hooks Reference

React Hooks参考

Built-in State Hooks

内置状态Hooks

useState

useState

Manage local component state for simple values.
Syntax:
javascript
const [state, setState] = useState(initialValue);
Basic Example:
javascript
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:
  • Use functional updates when new state depends on previous state
  • Keep state as local as possible
  • Don't store derived values in state
  • Initialize with functions for expensive computations
Multiple State Variables:
javascript
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:
javascript
function ExpensiveComponent() {
  // Function only runs once on initial render
  const [state, setState] = useState(() => {
    const initialValue = expensiveComputation();
    return initialValue;
  });

  return <div>{state}</div>;
}
管理组件的本地简单状态。
语法:
javascript
const [state, setState] = useState(initialValue);
基础示例:
javascript
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>
  );
}
最佳实践:
  • 当新状态依赖于前一个状态时,使用函数式更新
  • 尽可能将状态保持在本地
  • 不要在状态中存储派生值
  • 对于昂贵的计算,使用函数进行初始化
多状态变量:
javascript
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>
  );
}
惰性初始化:
javascript
function ExpensiveComponent() {
  // 函数仅在初始渲染时运行一次
  const [state, setState] = useState(() => {
    const initialValue = expensiveComputation();
    return initialValue;
  });

  return <div>{state}</div>;
}

useReducer

useReducer

Manage complex state logic with a reducer pattern (similar to Redux).
Syntax:
javascript
const [state, dispatch] = useReducer(reducer, initialState, init?);
Task Manager Example:
javascript
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:
  • Multiple related state values
  • Complex state update logic
  • State transitions follow predictable patterns
  • Need to optimize performance with many updates
  • Want to separate state logic from component
使用reducer模式管理复杂状态逻辑(类似Redux)。
语法:
javascript
const [state, dispatch] = useReducer(reducer, initialState, init?);
任务管理器示例:
javascript
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:
  • 多个相关的状态值
  • 复杂的状态更新逻辑
  • 状态转换遵循可预测的模式
  • 需要通过多次更新优化性能
  • 希望将状态逻辑与组件分离

useContext

useContext

Access context values without prop drilling.
Syntax:
javascript
const value = useContext(SomeContext);
Theme Context Example:
javascript
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:
javascript
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>
  );
}
无需Props透传即可访问上下文值。
语法:
javascript
const value = useContext(SomeContext);
主题上下文示例:
javascript
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:
javascript
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>
  );
}

Effect Hooks

副作用Hooks

useEffect

useEffect

Synchronize component with external systems (APIs, DOM, subscriptions).
Syntax:
javascript
useEffect(() => {
  // Effect logic
  return () => {
    // Cleanup logic
  };
}, [dependencies]);
Data Fetching Example:
javascript
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:
javascript
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:
javascript
// 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);
}, []);
使组件与外部系统(API、DOM、订阅)同步。
语法:
javascript
useEffect(() => {
  // 副作用逻辑
  return () => {
    // 清理逻辑
  };
}, [dependencies]);
数据获取示例:
javascript
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>;
}
订阅示例:
javascript
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>
  );
}
常见副作用模式:
javascript
// 在挂载时运行一次
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);
}, []);

useLayoutEffect

useLayoutEffect

Synchronous version of useEffect, runs before browser paint.
Use Cases:
  • Measuring DOM elements
  • Synchronous DOM mutations
  • Preventing visual flickering
Example:
javascript
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>
  );
}
useEffect的同步版本,在浏览器绘制前运行。
使用场景:
  • 测量DOM元素
  • 同步DOM修改
  • 防止视觉闪烁
示例:
javascript
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>
  );
}

Performance Hooks

性能优化Hooks

useMemo

useMemo

Cache expensive computations between renders.
Syntax:
javascript
const cachedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
Example:
javascript
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:
  • Expensive calculations (filtering, sorting large arrays)
  • Preventing unnecessary re-renders of child components
  • Stabilizing object/array references passed as dependencies
Anti-pattern (unnecessary):
javascript
// ❌ Don't use for simple calculations
const sum = useMemo(() => a + b, [a, b]); // Overkill

// ✅ Just compute directly
const sum = a + b;
在渲染之间缓存昂贵的计算结果。
语法:
javascript
const cachedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
示例:
javascript
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:
  • 昂贵的计算(过滤、排序大型数组)
  • 防止子组件不必要的重渲染
  • 稳定作为依赖传递的对象/数组引用
反模式(不必要的使用):
javascript
// ❌ 不要用于简单计算
const sum = useMemo(() => a + b, [a, b]); // 小题大做

// ✅ 直接计算即可
const sum = a + b;

useCallback

useCallback

Memoize function references between renders.
Syntax:
javascript
const cachedFn = useCallback(() => {
  // Function logic
}, [dependencies]);
Example:
javascript
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:
  • Passing callbacks to memoized child components
  • Function is a dependency of useEffect or other hooks
  • Optimizing expensive event handlers
在渲染之间缓存函数引用。
语法:
javascript
const cachedFn = useCallback(() => {
  // 函数逻辑
}, [dependencies]);
示例:
javascript
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:
  • 将回调传递给已记忆的子组件
  • 函数是useEffect或其他Hooks的依赖
  • 优化昂贵的事件处理函数

React.memo

React.memo

Memoize entire component to prevent unnecessary re-renders.
Syntax:
javascript
const MemoizedComponent = memo(function Component(props) {
  // Component logic
}, arePropsEqual?);
Example:
javascript
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;
  }
);
记忆整个组件以防止不必要的重渲染。
语法:
javascript
const MemoizedComponent = memo(function Component(props) {
  // 组件逻辑
}, arePropsEqual?);
示例:
javascript
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;
  }
);

Ref Hooks

Ref Hooks

useRef

useRef

Create mutable reference that persists across renders.
Syntax:
javascript
const ref = useRef(initialValue);
DOM Reference Example:
javascript
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:
javascript
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:
javascript
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>
  );
}
创建在渲染之间持久化的可变引用。
语法:
javascript
const ref = useRef(initialValue);
DOM引用示例:
javascript
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} />;
}
存储可变值:
javascript
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>
  );
}
前一个值模式:
javascript
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>
  );
}

Transition Hooks (React 18+)

过渡Hooks(React 18+)

useTransition

useTransition

Mark state updates as non-urgent, keeping UI responsive.
Syntax:
javascript
const [isPending, startTransition] = useTransition();
Example:
javascript
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>
  );
}
将状态更新标记为非紧急,保持UI响应性。
语法:
javascript
const [isPending, startTransition] = useTransition();
示例:
javascript
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>
  );
}

useDeferredValue

useDeferredValue

Defer updating part of the UI.
Syntax:
javascript
const deferredValue = useDeferredValue(value);
Example:
javascript
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)} />
    </>
  );
}
延迟更新UI的部分内容。
语法:
javascript
const deferredValue = useDeferredValue(value);
示例:
javascript
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)} />
    </>
  );
}

Server Action Hooks (RSC)

Server Action Hooks(RSC)

useActionState

useActionState

Manage server action state and pending status.
Syntax:
javascript
const [state, formAction, isPending] = useActionState(serverAction, initialState);
Example:
javascript
"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):
javascript
"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' };
  }
}
管理Server Action的状态和待处理状态。
语法:
javascript
const [state, formAction, isPending] = useActionState(serverAction, initialState);
示例:
javascript
"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):
javascript
"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 Patterns

自定义Hooks模式

Custom hooks let you extract and reuse stateful logic across components.
自定义Hooks允许你提取并在组件之间复用有状态逻辑。

Naming Convention

命名规范

Always prefix custom hooks with
use
:
javascript
useCustomHook // ✅ Correct
customHook    // ❌ Wrong
始终以
use
作为自定义Hooks的前缀:
javascript
useCustomHook // ✅ 正确
customHook    // ❌ 错误

Data Fetching Hook

数据获取Hook

javascript
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>;
}
javascript
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>;
}

Form Input Hook

表单输入Hook

javascript
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>
  );
}
javascript
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>
  );
}

Local Storage Hook

本地存储Hook

javascript
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>
  );
}
javascript
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>
  );
}

Debounce Hook

防抖Hook

javascript
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..."
    />
  );
}
javascript
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..."
    />
  );
}

Window Size Hook

窗口大小Hook

javascript
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>
  );
}
javascript
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>
  );
}

Online Status Hook

在线状态Hook

javascript
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>
  );
}
javascript
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>
  );
}

Component Patterns

组件模式

Compound Components

复合组件

Build components that work together while sharing implicit state.
javascript
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>
  );
}
构建协同工作并共享隐式状态的组件。
javascript
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>
  );
}

Render Props

渲染Props

Share code between components using a prop whose value is a function.
javascript
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>
        )
      )}
    />
  );
}
使用值为函数的Props在组件之间共享代码。
javascript
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>
        )
      )}
    />
  );
}

Higher-Order Components (HOC)

高阶组件(HOC)

Wrap components to enhance functionality.
javascript
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 />;
}
包装组件以增强功能。
javascript
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 />;
}

Container/Presenter Pattern

容器/展示器模式

Separate logic from presentation.
javascript
// 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>
  );
}
将逻辑与展示分离。
javascript
// 容器(逻辑)
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>
  );
}

Performance Optimization

性能优化

Identifying Performance Issues

识别性能问题

Use React DevTools Profiler to identify:
  • Components re-rendering unnecessarily
  • Expensive render operations
  • State update cascades
使用React DevTools Profiler识别:
  • 不必要重渲染的组件
  • 昂贵的渲染操作
  • 状态更新级联

Optimization Strategies

优化策略

1. Memoization

1. 记忆化

javascript
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>
  );
}
javascript
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>
  );
}

2. Code Splitting

2. 代码分割

javascript
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>
  );
}
javascript
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>
  );
}

3. Virtualization

3. 虚拟化

javascript
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>
  );
}
javascript
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>
  );
}

4. Avoid Anonymous Functions

4. 避免匿名函数

javascript
// ❌ 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>
  );
}
javascript
// ❌ 错误 - 每次渲染都会创建新函数
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>
  );
}

Server Components (RSC)

Server Components(RSC)

React Server Components render on the server, reducing client bundle size.
React Server Components在服务器端渲染,减少客户端包体积。

Server Component

Server组件

javascript
// 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;
javascript
// app/users/page.js(默认是Server组件)
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>
  );
}

export default UsersPage;

Client Component

Client组件

javascript
// 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>
  );
}
javascript
// components/Counter.js
"use client"; // 标记为客户端组件

import { useState } from 'react';

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

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

Composition Pattern

组合模式

javascript
// 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;
javascript
// app/page.js(Server组件)
import Counter from '@/components/Counter'; // Client组件

async function HomePage() {
  const data = await fetchData(); // 服务器端数据获取

  return (
    <div>
      <h1>{data.title}</h1>
      <p>{data.description}</p>
      <Counter /> {/* 交互式客户端组件 */}
    </div>
  );
}

export default HomePage;

State Management Strategies

状态管理策略

Local State (useState)

本地状态(useState)

Best for: Component-specific state
javascript
function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
最适合:组件特定的状态
javascript
function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}

Lifted State

提升状态

Best for: Shared state between sibling components
javascript
function Parent() {
  const [sharedValue, setSharedValue] = useState('');

  return (
    <>
      <ChildA value={sharedValue} onChange={setSharedValue} />
      <ChildB value={sharedValue} />
    </>
  );
}
最适合:兄弟组件之间共享状态
javascript
function Parent() {
  const [sharedValue, setSharedValue] = useState('');

  return (
    <>
      <ChildA value={sharedValue} onChange={setSharedValue} />
      <ChildB value={sharedValue} />
    </>
  );
}

Context API

Context API

Best for: App-wide state (theme, auth, language)
javascript
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>
  );
}
最适合:应用级状态(主题、认证、语言)
javascript
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>
  );
}

useReducer + Context

useReducer + Context

Best for: Complex state logic with multiple actions
javascript
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>
  );
}
最适合:具有多个动作的复杂状态逻辑
javascript
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>
  );
}

Best Practices

最佳实践

1. Component Design

1. 组件设计

  • Keep components small and focused (Single Responsibility)
  • Extract reusable logic into custom hooks
  • Prefer composition over inheritance
  • Use prop spreading sparingly
  • Define PropTypes or TypeScript types
  • 保持组件小巧且专注(单一职责)
  • 将可复用逻辑提取到自定义Hooks中
  • 优先使用组合而非继承
  • 谨慎使用Props展开
  • 定义PropTypes或TypeScript类型

2. State Management

2. 状态管理

  • Keep state as local as possible
  • Lift state only when necessary
  • Avoid unnecessary state (derive from props when possible)
  • Use reducers for complex state logic
  • Batch state updates when possible
  • 尽可能将状态保持在本地
  • 仅在必要时提升状态
  • 避免不必要的状态(尽可能从Props派生)
  • 对复杂状态逻辑使用reducer
  • 尽可能批量更新状态

3. Performance

3. 性能

  • Don't optimize prematurely - measure first
  • Use React DevTools Profiler
  • Memoize expensive computations
  • Avoid creating objects/arrays in render
  • Use key prop correctly for lists
  • 不要过早优化 - 先进行测量
  • 使用React DevTools Profiler
  • 对昂贵的计算进行记忆化
  • 避免在渲染中创建对象/数组
  • 为列表正确使用key属性

4. Effects

4. 副作用

  • Keep effects focused (one concern per effect)
  • Always specify dependencies
  • Clean up effects (return cleanup function)
  • Use AbortController for fetch requests
  • Avoid race conditions with ignore flags
  • 保持副作用专注(每个副作用处理一个关注点)
  • 始终指定依赖项
  • 清理副作用(返回清理函数)
  • 对fetch请求使用AbortController
  • 使用ignore标志避免竞态条件

5. Code Organization

5. 代码组织

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
src/
├── components/
│   ├── common/         # 可复用UI组件
│   ├── features/       # 特定功能组件
│   └── layout/         # 布局组件
├── hooks/              # 自定义Hooks
├── context/            # 上下文提供者
├── utils/              # 工具函数
├── services/           # API服务
└── types/              # TypeScript类型

Common Anti-Patterns to Avoid

需避免的常见反模式

1. Mutating State

1. 修改状态

javascript
// ❌ Wrong
const handleAdd = () => {
  items.push(newItem);
  setItems(items);
};

// ✅ Correct
const handleAdd = () => {
  setItems([...items, newItem]);
};
javascript
// ❌ 错误
const handleAdd = () => {
  items.push(newItem);
  setItems(items);
};

// ✅ 正确
const handleAdd = () => {
  setItems([...items, newItem]);
};

2. Missing Dependencies

2. 缺失依赖项

javascript
// ❌ Wrong
useEffect(() => {
  console.log(userId);
}, []); // Missing userId dependency

// ✅ Correct
useEffect(() => {
  console.log(userId);
}, [userId]);
javascript
// ❌ 错误
useEffect(() => {
  console.log(userId);
}, []); // 缺失userId依赖

// ✅ 正确
useEffect(() => {
  console.log(userId);
}, [userId]);

3. Conditional Hooks

3. 条件式Hooks

javascript
// ❌ Wrong
if (condition) {
  const [state, setState] = useState(0);
}

// ✅ Correct
const [state, setState] = useState(0);
if (condition) {
  // Use state
}
javascript
// ❌ 错误
if (condition) {
  const [state, setState] = useState(0);
}

// ✅ 正确
const [state, setState] = useState(0);
if (condition) {
  // 使用状态
}

4. Creating Components Inside Components

4. 在组件内部创建组件

javascript
// ❌ 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 />;
}
javascript
// ❌ 错误
function Parent() {
  function Child() { // 每次渲染都会重新创建
    return <div>Child</div>;
  }
  return <Child />;
}

// ✅ 正确
function Child() {
  return <div>Child</div>;
}

function Parent() {
  return <Child />;
}

Troubleshooting

故障排除

Common Issues

常见问题

Issue: Infinite re-render loop
  • Check useEffect dependencies
  • Avoid setting state directly in render
  • Use functional updates:
    setState(prev => prev + 1)
Issue: Stale closure
  • Use functional updates in setState
  • Add missing dependencies to useEffect
  • Use useRef for mutable values
Issue: Component not re-rendering
  • Check if state is being mutated (use immutable updates)
  • Verify React.memo comparison function
  • Ensure new reference for objects/arrays
Issue: Memory leak warning
  • Clean up effects (return cleanup function)
  • Cancel ongoing requests on unmount
  • Clear timers/intervals
问题:无限重渲染循环
  • 检查useEffect依赖项
  • 避免在渲染中直接设置状态
  • 使用函数式更新:
    setState(prev => prev + 1)
问题:过时闭包
  • 在setState中使用函数式更新
  • 为useEffect添加缺失的依赖项
  • 对可变值使用useRef
问题:组件不重渲染
  • 检查是否修改了状态(使用不可变更新)
  • 验证React.memo的比较函数
  • 确保对象/数组使用新引用
问题:内存泄漏警告
  • 清理副作用(返回清理函数)
  • 在卸载时取消正在进行的请求
  • 清除定时器/间隔器

Resources

资源


Skill Version: 1.0.0 Last Updated: October 2025 Skill Category: Frontend Development, React, JavaScript Compatible With: React 18+, Next.js 13+, TypeScript

技能版本:1.0.0 最后更新:2025年10月 技能分类:前端开发, React, JavaScript 兼容版本:React 18+, Next.js 13+, TypeScript