我们经常会看到这样的场景:一个组件改了 props,紧接着用 useEffect 重置 useState;或者每次函数都用 useCallback 包起来,生怕性能不够“丝滑”。其实,这些“反射式优化”不仅没有带来好处,反而拖慢了你的 UI,制造了更多不可预测的问题。
React 官方文档已经为这些常见问题给出了非常清晰的替代方案——你可能不需要 useEffect!👇
1. Effect重置状态
用Effect重置状态?你是在玩俄罗斯套娃渲染!
1.1 开局对比 state
❌错误示范:
function List({ items }) {
const [selection, setSelection] = useState(null);
useEffect(() => {
setSelection(null); // 每次items变就重置?渲染两次!
}, [items]);
}
后果:组件先用旧数据渲染 → 跑Effect → 再重新渲染 → 子组件连环爆炸!
✅正确姿势:直接在渲染时暴力重置!
function List({ items }) {
const [prevItems, setPrevItems] = useState(items);
if (items !== prevItems) {
setPrevItems(items);
setSelection(null); // 一步到位,拒绝套娃!
}
}
原理:React渲染阶段直接对比props,不!要!拖!到!Effect!
1.2 用key属性竟能秒杀嵌套状态
用key属性竟能秒杀嵌套状态?90%的人不知道!
❌错误示范:
// 用户ID变了?用Effect清空评论?太Low!
useEffect(() => setComment(""), [userId]);
后果:先渲染旧数据 → 再跑Effect → 再渲染新数据 → 性能直接扑街!
✅正确姿势:给组件一个key,让它原地去世重生!
export default function Profile({ userId }) {
return <ProfilePage key={userId} userId={userId} />; // key一变,组件直接重置!
function ProfilePage({ userId }) {
const [comment, setComment] = useState(""); // 自动清空,爽!
}
}
原理:key是组件的身份证号,一变就销毁旧组件,创建新实例,状态自动归零!
2. useEffect清理函数
🚨不写清理函数?无脑 useEffect 拉取数据也可能出锅!
❌错误示范:
useEffect(() => {
fetchResults(query).then(setResults); // 连续请求?后发的可能先到!
}, [query]);
后果:用户疯狂输入 → 请求乱序返回 → 页面显示错乱数据!
✅正确姿势:用ignore让陈年老请求自闭!
useEffect(() => {
let ignore = false;
fetchResults(query).then(json => {
if (!ignore) setResults(json); // 只认最后一个请求!
});
return () => { ignore = true; }; // 清理函数一键截胡!
}, [query]);
原理:Effect卸载时标记ignore=true,过时响应直接原地丢弃!
3. useCallback 并非万金油
很多同学以为“函数传 props 会导致子组件重新渲染”,于是每个函数都包 useCallback
,但其实:
- 如果组件没有对该函数依赖进行 memo 或
React.memo
优化,useCallback
根本不会带来任何性能提升; - 而多余的
useCallback
会增加思维负担,甚至带来 bugs(依赖数组写错了都不知道)。
❌错误示范:
const handleSubmit = (orderDetails) => { /* ... */ };
// 每次渲染都创建新函数,memo子组件白给了!
<ShippingForm onSubmit={handleSubmit} />
后果:子组件疯狂重渲染 → 性能优化了个寂寞!
✅正确姿势:useCallback锁死函数,依赖不变就复用!
const handleSubmit = useCallback((orderDetails) => {
post(`/products/${productId}/buy`, [referrer, orderDetails]);
}, [productId, referrer]); // 依赖不变,函数永远缓存!
原理:useCallback让函数身份不变,memo子组件直接跳过渲染!
React 团队不是让你少用 useEffect,是希望你更聪明地用 React 思维去解决问题。我们应该把副作用留给真正副作用的场景(如订阅、事件、请求) ,而不是用它来弥补对组件模型的理解盲区。
📎 参考资料
React 官方文档章节:You Might Not Need an Effect
📢 如果你觉得这篇文章对你有帮助,欢迎点赞 + 收藏 + 分享给更多 React 开发者 👇👇👇