我明明只是修改个状态,怎么页面开始疯狂抽搐甚至直接崩了?
作为前端开发者,相信很多人都曾在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的渲染过程分为几个阶段:
- Render阶段:计算虚拟DOM的变化
- Commit阶段:将变化应用到真实DOM
- 清理阶段:执行副作用和生命周期方法
当我们在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>
);
}
其他常见陷阱和解决方案
- 事件处理中的setState
这是setState最安全的调用场所
jsx
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(prevCount => prevCount + 1); // 正确:在事件处理中调用
};
return <button onClick={handleClick}>Count: {count}</button>;
}
- 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
最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!