React新文档:不要滥用useEffect哦
2023-10-16 22:58:14
React:useEffect 与性能优化
大家好,我是卡颂。你或你的同事在使用 useEffect
时有没有发生过以下场景:
- 当你希望状态
a
变化后发起请求,于是你使用了useEffect
:
useEffect(() => {
const fetchData = async () => {
const response = await fetch('https://example.com/api/data');
const data = await response.json();
setState({ data });
};
fetchData();
}, [a]);
这段代码运行符合预期,上线后也没问题。
- 随着需求不断迭代,你又希望状态
b
变化后也发起请求,于是你添加了第二个useEffect
:
useEffect(() => {
const fetchData = async () => {
const response = await fetch('https://example.com/api/data');
const data = await response.json();
setState({ data });
};
fetchData();
}, [a, b]);
你认为这个代码也应该运行良好,但事实是,这个 useEffect
每次都会触发,无论 a
还是 b
哪个发生变化。这导致了不必要的请求和性能问题。
为什么会出现这种问题?
要理解这个问题,我们需要从 useEffect
的内部实现入手。
useEffect 的内部实现
useEffect
是一个 React Hook,它允许你执行副作用操作,例如在组件挂载后发起请求、在组件卸载前进行清理工作等。
useEffect
的实现原理是利用 React 的 Diff 算法。当组件更新时,React 会比较新旧组件的 useEffect
数组。如果数组内容发生变化,React 就会调用 useEffect
回调函数,执行副作用操作。
在这个例子中,问题出在第二个 useEffect
的依赖数组 [a, b]
上。由于 a
和 b
都是组件的状态,所以每次组件更新时,[a, b]
都会发生变化,导致 useEffect
回调函数每次都会被调用。
解决方法
为了解决这个问题,我们需要让 useEffect
仅在 a
变化时触发。我们可以使用一个中间变量 prevA
来存储上一次 a
的值,然后在 useEffect
中比较 a
和 prevA
的值,只有当 a
发生变化时才执行副作用操作。
const prevA = useRef(a);
useEffect(() => {
if (prevA.current !== a) {
const fetchData = async () => {
const response = await fetch('https://example.com/api/data');
const data = await response.json();
setState({ data });
};
fetchData();
}
prevA.current = a;
}, [a]);
这个解决方案可以确保 useEffect
仅在 a
变化时触发,从而避免了不必要的请求和性能问题。
总结
在使用 useEffect
时,我们需要谨慎选择依赖数组,以避免不必要的副作用操作。否则,可能会导致性能问题。
在实际项目中,我们还可以通过以下方法来优化 useEffect
的性能:
- 使用
useCallback
和useMemo
来避免不必要的函数和对象重新创建。 - 使用
useEffect
的第二个参数cleanup
函数来进行清理工作,避免内存泄漏。 - 在某些情况下,我们可以使用
useReducer
来代替useEffect
,以获得更好的性能。
我希望这篇文章对你有帮助。如果你有任何问题,请随时留言。