react中useState、useEffect、useCallback、useMemo 的区别与使用场景。

一、核心概念与定位

这四个 Hooks 解决的是函数组件的不同核心问题,先通过表格快速区分:

Hook 核心作用 解决的问题 本质 / 返回值 关键依赖
useState 管理组件的状态,使函数组件拥有状态 函数组件无内置状态(替代 class 的 state) 返回 [状态, 状态更新函数] 无依赖,更新函数可缓存
useEffect 处理组件的副作用(异步、DOM 操作等) 模拟类组件生命周期,处理非渲染逻辑 无返回值(可返回清理函数) 依赖数组控制执行时机
useCallback 缓存函数引用,避免每次渲染重新创建函数 因函数引用变化导致子组件 / 依赖重复执行 返回缓存的函数 依赖数组控制缓存失效
useMemo 缓存计算结果,避免每次渲染重复计算 复杂计算导致的性能损耗 返回缓存的计算结果 依赖数组控制缓存失效

二、逐个拆解:用法 + 场景 + 示例

1. useState:状态管理的基础

核心逻辑
  • 函数组件的 "状态容器",调用后返回一个数组:[当前状态, 更新状态的函数]
  • 状态更新是异步的(批量更新),更新函数有两种写法:直接传值 / 传回调(获取上一次状态)。
适用场景
  • 组件内需要响应式变化的数据(如输入框值、开关状态、列表数据);
  • 替代类组件的 this.statethis.setState
示例

jsx

javascript 复制代码
import { useState } from 'react';

function Counter() {
  // 初始化状态:count=0,setCount是更新函数
  const [count, setCount] = useState(0);
  const [user, setUser] = useState({ name: '张三', age: 20 });

  // 方式1:直接传值更新(适合不依赖上一次状态)
  const handleAdd = () => setCount(count + 1);

  // 方式2:回调更新(适合依赖上一次状态,避免异步更新的坑)
  const handleAddSafe = () => setCount(prevCount => prevCount + 1);

  // 更新对象/数组(需创建新引用,不可直接修改原数据)
  const updateUser = () => setUser(prev => ({ ...prev, age: prev.age + 1 }));

  return (
    <div>
      <p>计数:{count}</p>
      <button onClick={handleAdd}>+1</button>
      <button onClick={handleAddSafe}>安全+1</button>
      <p>用户年龄:{user.age}</p>
      <button onClick={updateUser}>年龄+1</button>
    </div>
  );
}

2. useEffect:副作用处理

核心逻辑
  • "副作用" 指:数据请求、DOM 操作、事件绑定、定时器等非渲染相关的操作

  • 依赖数组是核心:

    • 无依赖:每次渲染后执行;
    • 空依赖 []:仅挂载时执行(对应 componentDidMount);
    • 有依赖 [a, b]:仅依赖项变化时执行(对应 componentDidUpdate);
    • 返回的清理函数:卸载 / 依赖变化前执行(对应 componentWillUnmount)。
适用场景
  • 组件挂载后初始化(请求数据、绑定事件);
  • 依赖变化后更新(如 ID 变化重新请求数据);
  • 组件卸载前清理(取消请求、解绑事件、清除定时器)。
示例

jsx

javascript 复制代码
import { useState, useEffect } from 'react';

function DataList({ id }) {
  const [data, setData] = useState([]);

  useEffect(() => {
    // 1. 发起请求(副作用操作)
    const controller = new AbortController(); // 用于取消请求
    fetch(`/api/data/${id}`, { signal: controller.signal })
      .then(res => res.json())
      .then(setData)
      .catch(err => console.log('请求取消', err));

    // 2. 绑定事件
    const handleResize = () => console.log('窗口变化');
    window.addEventListener('resize', handleResize);

    // 3. 清理函数:卸载/依赖变化时执行
    return () => {
      controller.abort(); // 取消请求
      window.removeEventListener('resize', handleResize); // 解绑事件
    };
  }, [id]); // 仅 id 变化时重新执行

  return <div>{data.map(item => <p key={item.id}>{item.name}</p>)}</div>;
}

3. useCallback:缓存函数引用

核心逻辑
  • 函数组件每次渲染时,内部定义的函数会重新创建新的引用
  • useCallback 会缓存函数引用,只有当依赖数组变化时,才会返回新的函数;
  • 核心价值:配合 React.memo 避免子组件不必要的重渲染。
适用场景
  • 传递给子组件的回调函数(尤其是子组件用 React.memo 优化时);
  • 作为 useEffect 的依赖项(避免因函数引用变化导致 useEffect 重复执行)。
示例

jsx

javascript 复制代码
import { useState, useCallback, memo } from 'react';

// 子组件:用 React.memo 包裹,仅 props 变化时重渲染
const Child = memo(({ onClick, data }) => {
  console.log('子组件渲染');
  return <button onClick={onClick}>{data}</button>;
});

function Parent() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('张三');

  // 未缓存的函数:每次渲染都会创建新引用 → 子组件每次都重渲染
  const handleClickUncached = () => console.log(name);

  // 缓存的函数:仅 name 变化时才创建新引用 → 子组件仅 name 变化时重渲染
  const handleClickCached = useCallback(() => {
    console.log(name);
  }, [name]); // 依赖 name

  return (
    <div>
      <p>计数:{count}</p>
      <button onClick={() => setCount(count + 1)}>修改count</button>
      {/* 点击修改count时,子组件1会重渲染,子组件2不会 */}
      <Child onClick={handleClickUncached} data="未缓存函数" />
      <Child onClick={handleClickCached} data="缓存函数" />
    </div>
  );
}

4. useMemo:缓存计算结果

核心逻辑
  • 函数组件每次渲染时,内部的复杂计算会重新执行
  • useMemo 会缓存计算结果,只有当依赖数组变化时,才会重新计算;
  • 注意:useMemo性能优化手段,不要滥用(简单计算无需缓存)。
适用场景
  • 复杂计算(如大数据排序、过滤、深拷贝);
  • 避免每次渲染都创建新的对象 / 数组(作为子组件 props 时);
  • 替代 useCallback 缓存函数(useMemo(() => () => {}, []) 等价于 useCallback(() => {}, []))。
示例

jsx

javascript 复制代码
import { useState, useMemo } from 'react';

function BigList() {
  const [list, setList] = useState(Array(10000).fill(0).map((_, i) => i));
  const [keyword, setKeyword] = useState('');

  // 复杂计算:过滤大数据列表 → 用 useMemo 缓存结果
  const filteredList = useMemo(() => {
    console.log('执行过滤计算');
    return list.filter(item => item.toString().includes(keyword));
  }, [list, keyword]); // 依赖 list 和 keyword

  // 简单计算:无需 useMemo(缓存开销 > 计算开销)
  const simpleCalc = () => keyword.length * 2;

  return (
    <div>
      <input value={keyword} onChange={(e) => setKeyword(e.target.value)} />
      <div>
        {filteredList.map(item => <p key={item}>{item}</p>)}
      </div>
    </div>
  );
}

三、关键避坑点

  1. useState 坑 :更新对象 / 数组时,必须创建新引用(不可直接修改 prevState);

  2. useEffect 坑:依赖数组必须声明所有用到的变量 / 函数(否则会捕获旧值);

  3. useCallback/useMemo 坑

    • 不要缓存简单函数 / 计算(优化收益 < 缓存开销);
    • 依赖数组要精准(漏写依赖会导致缓存值过期);
  4. 优先级 :先保证功能正确,再用 useCallback/useMemo 做性能优化。

总结

  1. useState:管理组件状态,是函数组件拥有响应式数据的基础;
  2. useEffect:处理副作用,控制代码的执行时机(模拟生命周期);
  3. useCallback :缓存函数引用,优化子组件重渲染或 useEffect 依赖;
  4. useMemo:缓存计算结果,避免复杂计算重复执行,提升渲染性能。

核心原则:useState/useEffect 是函数组件的 "基础必备",useCallback/useMemo 是 "性能优化工具",按需使用而非无脑添加。

相关推荐
chao_6666661 天前
React Native + Expo 开发指南:编译、调试、构建全解析
javascript·react native·react.js
码丁_1171 天前
某it培训机构前端三阶段react及新增面试题
前端·react.js·前端框架
_pengliang1 天前
react native ios 2个modal第二个不显示
javascript·react native·react.js
我算哪枝小绿植1 天前
react实现日历拖拽效果
前端·react.js·前端框架
OEC小胖胖1 天前
04|从 Lane 位图到 `getNextLanes`:React 更新优先级与纠缠(Entangle)模型
前端·react.js·前端框架
愤怒的可乐1 天前
从零构建大模型智能体:ReAct 智能体实战
前端·react.js·前端框架
BlackWolfSky1 天前
React中文网课程笔记4—常用工具配置
前端·笔记·react.js
巾帼前端1 天前
前端框架 React 的虚拟 DOM是如何在这一层层抽象中定位自己位置的?
前端·react.js·前端框架
wayne2141 天前
React Native 0.80 学习参考:一个完整可运行的实战项目
学习·react native·react.js