React 常用 Hooks 详细解析

Source

React Hooks 是 React 16.8 引入的核心特性,允许在函数组件中使用状态、生命周期等特性,替代类组件的复杂逻辑。以下是常用 Hooks 的详细解析及最佳实践:


1. useState:状态管理

用途:在函数组件中定义和更新局部状态。
示例

import {
    
       useState } from 'react';

const Counter = () => {
    
      
  const [count, setCount] = useState(0); // 初始值 0

  return (
    <button onClick={
    
      () => setCount(count + 1)}>
      Clicked {
    
      count} times
    </button>
  );
};

注意

  • 状态更新是异步的,连续调用 setCount(count + 1) 不会立即生效。
  • 若新状态依赖旧状态,应使用函数形式:setCount(prev => prev + 1)

2. useEffect:副作用处理

用途:处理组件生命周期中的副作用(如数据请求、DOM 操作、订阅)。
示例

import {
    
       useEffect, useState } from 'react';

const DataFetcher = ({
     
        url }) => {
    
      
  const [data, setData] = useState(null);

  useEffect(() => {
    
      
    const fetchData = async () => {
    
      
      const response = await fetch(url);
      setData(await response.json());
    };
    fetchData();
  }, [url]); // 依赖项:url 变化时重新执行

  return <div>{
    
      data ? data.name : 'Loading...'}</div>;
};

注意

  • 依赖项数组
    • 空数组 []:仅在组件挂载时执行(类似 componentDidMount)。
    • 无数组:每次渲染后都执行(慎用)。
    • 包含变量:变量变化时重新执行。
  • 清理函数:返回一个函数用于清理(如取消订阅、移除事件监听):
    useEffect(() => {
          
            
      const timer = setInterval(() => {
          
            }, 1000);
      return () => clearInterval(timer); // 组件卸载时清理
    }, []);
    

3. useContext:跨组件数据传递

用途:在组件树中共享数据,避免逐层传递 props。
示例

import {
    
       createContext, useContext } from 'react';

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

// 2. 提供数据
const App = () => (
  <ThemeContext.Provider value="dark">
    <Toolbar />
  </ThemeContext.Provider>
);

// 3. 消费数据
const Toolbar = () => {
    
      
  const theme = useContext(ThemeContext);
  return <div>Current theme: {
    
      theme}</div>;
};

4. useReducer:复杂状态逻辑

用途:类似 Redux 的状态管理,适合多状态关联或复杂更新逻辑。
示例

import {
    
       useReducer } from 'react';

const initialState = {
    
       count: 0 };

function reducer(state, action) {
    
      
  switch (action.type) {
    
      
    case 'increment':
      return {
    
       count: state.count + 1 };
    case 'decrement':
      return {
    
       count: state.count - 1 };
    default:
      throw new Error();
  }
}

const Counter = () => {
    
      
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {
    
      state.count}
      <button onClick={
    
      () => dispatch({
    
       type: 'increment' })}>+</button>
      <button onClick={
    
      () => dispatch({
    
       type: 'decrement' })}>-</button>
    </>
  );
};

适用场景:状态更新涉及多个子值、依赖前一个状态、需要集中化管理。


5. useCallbackuseMemo:性能优化

useCallback:缓存函数
const handleClick = useCallback(() => {
    
      
  console.log('Clicked:', count);
}, [count]); // count 变化时重新创建函数
useMemo:缓存计算结果
const expensiveValue = useMemo(() => {
    
      
  return computeExpensiveValue(a, b);
}, [a, b]); // a/b 变化时重新计算

最佳实践

  • 仅当子组件依赖这些值且用 React.memo 优化时使用。
  • 避免滥用,内存缓存本身也有开销。

6. useRef:持久化引用

用途

  • 访问 DOM 元素。
  • 保存可变值(类似类组件的实例变量),不会触发重新渲染。
    示例
const TextInput = () => {
    
      
  const inputRef = useRef(null);

  const focusInput = () => {
    
      
    inputRef.current.focus();
  };

  return (
    <>
      <input ref={
    
      inputRef} />
      <button onClick={
    
      focusInput}>Focus</button>
    </>
  );
};

7. useLayoutEffect:同步副作用

用途:与 useEffect 类似,但会在 DOM 更新后同步执行(在浏览器绘制前)。
适用场景:需要直接操作 DOM 并确保用户看不到中间状态(如测量元素尺寸)。
示例

useLayoutEffect(() => {
    
      
  const {
    
       width } = divRef.current.getBoundingClientRect();
  setWidth(width); // 确保在渲染前获取最新尺寸
}, []);

8. 自定义 Hook:逻辑复用

用途:将组件逻辑封装为可复用的函数。
示例:自定义 useFetch

const useFetch = (url) => {
    
      
  const [data, setData] = useState(null);

  useEffect(() => {
    
      
    const fetchData = async () => {
    
      
      const response = await fetch(url);
      setData(await response.json());
    };
    fetchData();
  }, [url]);

  return data;
};

// 使用
const MyComponent = () => {
    
      
  const data = useFetch('/api/data');
  return <div>{
    
      data}</div>;
};

Hooks 使用规则

  1. 只在顶层调用:不能在条件、循环或嵌套函数中使用 Hooks。
  2. 仅在 React 函数组件或自定义 Hook 中使用

总结

Hook 核心用途 典型场景
useState 管理组件内部状态 计数器、表单输入
useEffect 处理副作用(数据请求、订阅) API 调用、事件监听
useContext 跨组件共享数据 主题、用户身份全局传递
useReducer 复杂状态逻辑管理 表单多字段、状态机
useCallback 缓存函数,避免子组件无效渲染 传递回调函数给优化过的子组件
useMemo 缓存计算结果,减少重复计算 复杂计算、优化渲染性能
useRef 访问 DOM 或保存可变引用 输入框聚焦、保存定时器 ID
useLayoutEffect 同步 DOM 操作 测量元素尺寸、强制同步更新

合理使用 Hooks 能显著提升代码可读性和可维护性,但需注意避免过度优化和滥用内存缓存。