React中useEffect的5个核心应用场景与避坑指南

更新日期: 2025-06-11阅读: 40标签: 场景

react函数式组件开发中,useEffect是处理副作用的基石。但许多开发者仅停留在基础用法,未能充分发挥其价值。本文将深入剖析5个高频应用场景,同时纠正常见误解,助你写出更健壮的组件。


一、副作用清理:避免内存泄漏的关键

误区修正:useEffect回调并非在“挂载完成时”执行,而是在浏览器完成布局与绘制后(提交阶段)异步执行。

典型场景

const ScrollTracker = () => {
  useEffect(() => {
    const handleScroll = () => console.log(window.scrollY);
    
    // 事件绑定
    window.addEventListener('scroll', handleScroll);
    
    // 清理函数(组件卸载时执行)
    return () => window.removeEventListener('scroll', handleScroll);
  }, []); // 空依赖确保只运行一次

  return <div>滚动监听器...</div>;
};

避坑指南

  • 定时器、WebSocket连接、dom事件必须清理
  • 清理函数不仅卸载时执行,在依赖变更导致重执行前也会触发


二、表单初始化:异步数据处理的正确姿势

代码问题:initValue可能为undefined或null,直接初始化状态会导致后续更新失效

优化方案

const FormInput = ({ initialValue }) => {
  const [value, setValue] = useState(null);
  const isInitialized = useRef(false);

  useEffect(() => {
    // 仅当获取到有效初始值且未初始化时更新
    if (initialValue !== undefined && !isInitialized.current) {
      isInitialized.current = true;
      setValue(initialValue);
    }
  }, [initialValue]);

  return <input value={value ?? ''} onChange={e => setValue(e.target.value)} />;
};

最佳实践

  • 使用useRef标记初始化状态而非useState,避免额外渲染
  • 显式处理undefined/null等边界值


三、依赖变更监听:跳过初始执行的两种方案

场景需求:仅在特定状态变更时执行逻辑,忽略首次渲染

方案1:使用初始化标记

const PriceWatcher = ({ price }) => {
  const isFirstRender = useRef(true);

  useEffect(() => {
    if (isFirstRender.current) {
      isFirstRender.current = false;
      return;
    }
    alert(`价格更新至:${price}`);
  }, [price]);
};

方案2:自定义Hook封装(推荐)

function useUpdateEffect(callback, deps) {
  const isFirst = useRef(true);

  useEffect(() => {
    if (isFirst.current) {
      isFirst.current = false;
      return;
    }
    return callback();
  }, deps);
}

// 使用
useUpdateEffect(() => alert(`价格更新:${price}`), [price]);


四、DOM操作:同步与异步场景全解析

重要原则:useEffect中可安全访问已提交的DOM

同步渲染场景

const ElementMeasurer = () => {
  const divRef = useRef(null);

  useEffect(() => {
    // 此处可获取真实DOM尺寸
    console.log('元素宽度:', divRef.current.offsetWidth);
  }, []);

  return <div ref={divRef}>测量目标</div>;
};

异步数据渲染场景

const DynamicList = () => {
  const [items, setItems] = useState([]);
  const itemRefs = useRef([]);

  useEffect(() => {
    fetch('/api/items').then(res => setItems(res.data));
  }, []);

  useEffect(() => {
    if (items.length > 0) {
      // 动态渲染后计算首个元素位置
      const firstItemTop = itemRefs.current[0]?.getBoundingClientRect().top;
      console.log('首项位置:', firstItemTop);
    }
  }, [items]); // 依赖项触发DOM更新后计算

  return (
    <div>
      {items.map((item, index) => (
        <div 
          key={item.id}
          ref={el => itemRefs.current[index] = el}
        >
          {item.name}
        </div>
      ))}
    </div>
  );
};


五、进阶技巧:依赖项优化的核心原则

黄金法则

  • 所有回调内使用的状态/props都必须声明为依赖项
  • 空数组依赖([])仅用于纯初始化逻辑
  • 函数依赖需用useCallback包裹避免无限循环

示例

const SearchBox = ({ fetchData }) => {
  const [query, setQuery] = useState('');

  // 正确:稳定函数引用
  const throttledFetch = useCallback(throttle(fetchData, 500), [fetchData]);

  useEffect(() => {
    if (query) throttledFetch(query);
  }, [query, throttledFetch]); // 安全依赖

  return <input value={query} onChange={e => setQuery(e.target.value)} />;
};


六、常见陷阱与解决方案

无限循环陷阱

  • 成因:在effect中直接更新依赖项状态
  • 修复:检查是否存在setState→触发effect→再次setState的死循环

过时闭包问题

useEffect(() => {
  const timer = setInterval(() => {
    // 错误:始终读取初始state
    console.log(count); 
  }, 1000);
  return () => clearInterval(timer);
}, []); // 缺少count依赖

修复方案

  • 添加依赖项 + 清理重建
  • 使用useRef保存最新值


结语:掌握useEffect的思维模型

理解useEffect的核心在于建立渲染与副作用的关系

  • 每次渲染都有独立的Props/State
  • Effect是渲染结果的组成部分
  • 清理机制保证副作用的有序性

通过合理运用依赖项控制、清理函数和初始化策略,可解决90%的React副作用管理需求。建议结合React DevTools的useEffect执行日志分析优化时机,构建高性能应用。

链接: https://fly63.com/article/detial/12767

async/await 异步应用的常用场景

async/await 语法用看起来像写同步代码的方式来优雅地处理异步操作,但是我们也要明白一点,异步操作本来带有复杂性,像写同步代码的方式并不能降低本质上的复杂性,所以在处理上我们要更加谨慎, 稍有不慎就可能写出不是预期执行的代码,从而影响执行效率

vue中$refs, $emit, $on, $once, $off的使用

$refs的使用场景:父组件调用子组件的方法,可以传递数据。$emit的使用:子组件调用父组件的方法并传递数据。$on的使用场景:兄弟组件之间相互传递数据

php中-> 、=>、::、$this->使用方法与场景

->用来引用一个类的属性(变量)、方法(函数);=>是用来定义数组用的;::用来直接调用类中的属性或方法,没有实例化;$this->表示实例化后调用具体对象

react之Fragments使用方法及使用场景

React 中的一个常见模式是一个组件返回多个元素。Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点。还有一种新的短语法可用于声明它们。

html5不常用标签应用场景

作为一个前端开发,在浏览别人家的页面时总是会习惯性的查看他们页面的源码,发现大多数网站的页面,包括我自己写的页面中用到的最多的布局元素无外乎就是div、p、span、ul、dl、ol、li、dt、dd、strong、b

Js中的this原理及6种常见使用场景

this是JavaScript的一个关键字,函数调用时才会出现;因为函数是在一定的环境中运行的,调用函数时肯定需要知道是[谁调用的]?就用到了this进行指向;this 既不指向函数自身,也不指函数的词法作用域,而是调用函数时的对象!

HTML 元素标签语义化及使用场景

标签语义化就是让元素标签做适当的事情。例如 p 标签就是代表文本,button 标签代表按钮,nav 标签代表导航等等。其实标签语义化是给浏览器和搜索引擎看的。

JS中try-catch代码块的应用场景

try-catch属于同步代码块,因此无法捕获异步(重新开辟的线程,例如定时器,异步请求)代码中的异常,即能被try-catch捕获的异常,必须是在报错时候,线程的执行进入了try-catch代码块时,才能被捕获异常

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!