React 常用 Hooks 使用指南

useState、useEffect、useMemo、useCallback、useRef、useContext 的用法与示例

问题

介绍 React 中六个常用 Hooks 的用法:useState、useEffect、useMemo、useCallback、useRef、useContext。

解答

useState

管理组件状态。

import { useState } from 'react';

function Counter() {
  // 声明状态变量 count,初始值为 0
  const [count, setCount] = useState(0);

  // 函数式更新:基于前一个状态计算新状态
  const increment = () => setCount(prev => prev + 1);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>+1</button>
    </div>
  );
}

useEffect

处理副作用:数据请求、订阅、DOM 操作等。

import { useState, useEffect } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    // 副作用:请求数据
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => setUser(data));

    // 清理函数:组件卸载或 userId 变化时执行
    return () => {
      console.log('cleanup');
    };
  }, [userId]); // 依赖数组:userId 变化时重新执行

  return <div>{user?.name}</div>;
}

useMemo

缓存计算结果,避免重复计算。

import { useState, useMemo } from 'react';

function ExpensiveList({ items, filter }) {
  // 只有 items 或 filter 变化时才重新计算
  const filteredItems = useMemo(() => {
    console.log('filtering...');
    return items.filter(item => item.includes(filter));
  }, [items, filter]);

  return (
    <ul>
      {filteredItems.map(item => <li key={item}>{item}</li>)}
    </ul>
  );
}

useCallback

缓存函数引用,避免子组件不必要的重渲染。

import { useState, useCallback, memo } from 'react';

// 子组件用 memo 包裹,只有 props 变化才重渲染
const Button = memo(({ onClick, children }) => {
  console.log('Button render');
  return <button onClick={onClick}>{children}</button>;
});

function Parent() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState('');

  // 缓存函数,count 变化时才创建新函数
  const increment = useCallback(() => {
    setCount(c => c + 1);
  }, []);

  return (
    <div>
      <input value={text} onChange={e => setText(e.target.value)} />
      <Button onClick={increment}>Count: {count}</Button>
    </div>
  );
}

useRef

保存可变值,不触发重渲染;访问 DOM 元素。

import { useRef, useEffect } from 'react';

function TextInput() {
  // 引用 DOM 元素
  const inputRef = useRef(null);
  // 保存可变值(不触发重渲染)
  const renderCount = useRef(0);

  useEffect(() => {
    // 组件挂载后自动聚焦
    inputRef.current.focus();
    // 记录渲染次数
    renderCount.current += 1;
  });

  return (
    <div>
      <input ref={inputRef} placeholder="自动聚焦" />
      <p>渲染次数: {renderCount.current}</p>
    </div>
  );
}

useContext

跨组件传递数据,避免 props 层层传递。

import { createContext, useContext, useState } from 'react';

// 1. 创建 Context
const ThemeContext = createContext('light');

// 2. 子组件消费 Context
function ThemedButton() {
  const theme = useContext(ThemeContext);
  return (
    <button style={{
      background: theme === 'dark' ? '#333' : '#fff',
      color: theme === 'dark' ? '#fff' : '#333'
    }}>
      当前主题: {theme}
    </button>
  );
}

// 3. 父组件提供 Context
function App() {
  const [theme, setTheme] = useState('light');

  return (
    <ThemeContext.Provider value={theme}>
      <ThemedButton />
      <button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
        切换主题
      </button>
    </ThemeContext.Provider>
  );
}

关键点

  • useState:状态更新是异步的,用函数式更新获取最新值
  • useEffect:依赖数组为空 [] 只执行一次,不传则每次渲染都执行
  • useMemo:缓存值,适合计算开销大的场景
  • useCallback:缓存函数,配合 memo 优化子组件渲染
  • useRef.current 修改不触发重渲染,常用于存 DOM 引用或定时器 ID
  • useContext:适合全局状态(主题、用户信息),频繁更新的状态不适合用 Context