在使用 React 18 的并发渲染(Concurrent Rendering)特性时,为什么某些 useEffect 中的副作用可能被执行多次,如何正确处理这种情况?
在使用 React 18 的并发渲染(Concurrent Rendering)特性时,为什么某些 useEffect 中的副作用可能被执行多次,如何正确处理这种情况?
回答与解析:
React 18 引入了并发渲染(Concurrent Rendering)机制,允许 React 在渲染过程中中断、暂停或重排任务,以优先处理高优先级的更新(如用户输入)。在开发模式下,React 还启用了严格模式(Strict Mode),会故意对组件进行双重渲染(double rendering)——即 mount → unmount → mount,以此帮助开发者发现潜在的副作用问题。
这导致 useEffect 中的副作用(如数据获取、订阅、手动 DOM 操作等)可能被多次调用。虽然在生产环境中通常不会重复执行(除非并发中断后重试),但开发者仍需确保副作用是幂等的(多次执行不会导致错误或状态不一致)或正确清理。
正确处理方式包括:
-
使用清理函数(cleanup function):在 useEffect 中返回一个清理函数,用于取消订阅、清除定时器等,避免内存泄漏或竞态条件。
useEffect(() => { const timer = setTimeout(() => { console.log('Executed'); }, 1000); return () => clearTimeout(timer); // 清理 }, []); -
使用 useRef 跳过重复逻辑(谨慎使用):对于某些只应在首次挂载时执行的操作(如初始化第三方库),可通过 ref 标记是否已执行。
const hasMounted = useRef(false); useEffect(() => { if (!hasMounted.current) { // 初始化逻辑 hasMounted.current = true; } }, []);但这种做法违背了 React 的响应式原则,应尽量避免。
-
设计幂等的副作用:确保多次执行副作用不会产生副作用(如重复添加监听器、发送重复请求等)。
-
使用 useInsertionEffect(仅限 CSS-in-JS 库):对于需要同步插入样式的场景,可使用 useInsertionEffect,但它不适合一般逻辑。
总之,应遵循 React 的“副作用应可被清理和重复执行”的原则,而不是试图绕过并发渲染机制。这有助于构建更健壮、可预测的应用。

发表评论 (审核通过后显示评论):