为什么在render里调setState,代码会和你“翻脸”?

我明明只是修改个状态,怎么页面开始疯狂抽搐甚至直接崩了?

作为前端开发者,相信很多人都曾在React的render函数中尝试过setState,结果却遇到了意想不到的问题。今天小杨就来和大家聊聊这背后的原因,以及如何避免这个常见的陷阱。

一个亲身经历的bug案例

前几天我在开发一个商品列表组件时,遇到了一个奇怪的问题。页面在加载时会不断刷新,最后甚至直接白屏了。经过一番排查,我发现问题出在了下面这段代码:

jsx 复制代码
function ProductList({ products }) {
  const [filteredProducts, setFilteredProducts] = useState(products);
  const [sortOrder, setSortOrder] = useState('asc');
  
  // 这里犯了低级错误!
  if (products.length !== filteredProducts.length) {
    setFilteredProducts(products);
  }
  
  return (
    <div>
      {/* 渲染产品列表 */}
    </div>
  );
}

看起来我只是在props变化时更新state,但这样做却导致了组件的无限重新渲染!

为什么render中不能调用setState?

简单来说,在render过程中调用setState就像是在盖房子时不断修改蓝图------工程永远无法完工,反而可能把工地搞得一团糟。

React的渲染过程分为几个阶段:

  1. Render阶段:计算虚拟DOM的变化
  2. Commit阶段:将变化应用到真实DOM
  3. 清理阶段:执行副作用和生命周期方法

当我们在render函数中调用setState时,React会:

  • 标记需要更新状态
  • 重新执行render函数
  • 发现又调用了setState
  • 再次标记需要更新状态
  • ...形成无限循环

正确的解决方案

对于上面的问题,我最终使用了useEffect来避免在render中直接调用setState:

jsx 复制代码
function ProductList({ products }) {
  const [filteredProducts, setFilteredProducts] = useState(products);
  const [sortOrder, setSortOrder] = useState('asc');
  
  // 使用useEffect在适当的时候更新状态
  useEffect(() => {
    setFilteredProducts(products);
  }, [products]);
  
  const sortedProducts = useMemo(() => {
    return [...filteredProducts].sort((a, b) => {
      return sortOrder === 'asc' ? a.price - b.price : b.price - a.price;
    });
  }, [filteredProducts, sortOrder]);
  
  return (
    <div>
      {/* 渲染产品列表 */}
    </div>
  );
}

其他常见陷阱和解决方案

  1. 事件处理中的setState
    这是setState最安全的调用场所
jsx 复制代码
function Counter() {
  const [count, setCount] = useState(0);
  
  const handleClick = () => {
    setCount(prevCount => prevCount + 1); // 正确:在事件处理中调用
  };
  
  return <button onClick={handleClick}>Count: {count}</button>;
}
  1. useEffect中的setState
    需要注意依赖项数组,避免无限循环
jsx 复制代码
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    fetchUser(userId).then(userData => {
      setUser(userData); // 正确:在useEffect中调用
    });
  }, [userId]); // 确保依赖项正确
}

总结一下

  • 🚫 避免在render函数中直接调用setState
  • ✅ 在事件处理程序或useEffect中调用setState
  • 🔄 注意setState可能引起的重新渲染,合理使用useMemo和useCallback优化性能

⭐ 写在最后

请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.

✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式

✅ 认为我部分代码过于老旧,可以提供新的API或最新语法

✅ 对于文章中部分内容不理解

✅ 解答我文章中一些疑问

✅ 认为某些交互,功能需要优化,发现BUG

✅ 想要添加新功能,对于整体的设计,外观有更好的建议

✅ 一起探讨技术加qq交流群:906392632

最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!

相关推荐
Zuckjet_14 分钟前
开启 3D 之旅 - 你的第一个 WebGL 三角形
前端·javascript·3d·webgl
2401_8638014620 分钟前
探索 12 种 3D 文件格式:综合指南
前端·3d
珍宝商店2 小时前
前端老旧项目全面性能优化指南与面试攻略
前端·面试·性能优化
bitbitDown2 小时前
四年前端分享给你的高效开发工具库
前端·javascript·vue.js
YAY_tyy2 小时前
【JavaScript 性能优化实战】第六篇:性能监控与自动化优化
javascript·性能优化·自动化
gnip3 小时前
实现AI对话光标跟随效果
前端·javascript
脑花儿4 小时前
ABAP SMW0下载Excel模板并填充&&剪切板方式粘贴
java·前端·数据库
闭着眼睛学算法4 小时前
【华为OD机考正在更新】2025年双机位A卷真题【完全原创题解 | 详细考点分类 | 不断更新题目 | 六种主流语言Py+Java+Cpp+C+Js+Go】
java·c语言·javascript·c++·python·算法·华为od
烛阴4 小时前
【TS 设计模式完全指南】构建你的专属“通知中心”:深入观察者模式
javascript·设计模式·typescript
lumi.5 小时前
Vue.js 从入门到实践1:环境搭建、数据绑定与条件渲染
前端·javascript·vue.js