React 18+ 并发模式异常
文章目录
- 什么是 “并发模式异常”?
- 并发模式异常的特点
- 如何捕获并发模式异常?
- 1. 错误边界(Error Boundary)
- 2. 全局错误监听兜底
- 3. 处理 useTransition 中的异常
- 总结
在 React 18+ 中,“并发模式(Concurrent Mode)” 是一种允许 React 中断、暂停、恢复甚至放弃渲染工作的底层机制(注意:React 18 中并未直接暴露 “并发模式” 的开关,而是通过 createRoot、useTransition 等 API 隐式启用)。它的核心是让 React 能够 “非阻塞” 地处理渲染,优先响应高优先级任务(如用户输入),延迟低优先级任务(如大型列表渲染),从而提升应用的交互流畅度。
什么是 “并发模式异常”?
并发模式下的异常,指的是在并发渲染过程中(尤其是 “后台渲染未提交到 DOM” 的阶段)抛出的错误
。这类异常与传统 “同步渲染模式” 的异常有本质区别:
传统同步渲染
:渲染过程是 “一次性、不可中断” 的,一旦组件抛出错误,会立即冒泡到错误边界(Error Boundary),触发错误处理。并发渲染
:渲染可能分为 “准备阶段”(在内存中计算新的组件树,但未更新 DOM)和 “提交阶段”(将计算好的结果更新到 DOM)。如果错误发生在准备阶段(未提交到 DOM),传统错误边界可能无法捕获 —— 因为此时错误还未关联到实际的 DOM 树,错误边界只能捕获 “已提交到 DOM 的子树” 中的错误。
并发模式异常的特点
- 异步性:错误可能在 “后台渲染”(与主线程并行的低优先级任务)中抛出,时机不确定,传统同步 try/catch 难以覆盖。
- 未提交阶段的错误:如果渲染在准备阶段出错(未到达提交阶段),React 会直接放弃此次渲染,不会更新 DOM,但错误可能不会触发错误边界(因为错误边界只监听已提交的子树)。
- 与优先级相关:通过 useTransition 或 startTransition 标记的低优先级更新,其渲染过程可能被高优先级任务(如点击、输入)中断,中断时若抛出错误,处理逻辑更复杂。
如何捕获并发模式异常?
React 18 并未为并发模式异常提供全新的 API,但需要结合现有机制优化捕获逻辑:
1. 错误边界(Error Boundary)
错误边界依然是捕获 “已提交到 DOM 的组件树” 异常的核心方式,包括并发模式下 “成功提交后” 的错误:
class ErrorBoundary extends React.Component {state = { hasError: false };static getDerivedStateFromError() {return { hasError: true };}componentDidCatch(error, errorInfo) {console.error('已提交的并发渲染错误:', error, errorInfo);// 上报错误}render() {return this.state.hasError ? <h2>出错了</h2> : this.props.children;}
}// 使用:包裹可能在并发渲染中出错的组件
function App() {return (<ErrorBoundary><ConcurrentComponent /> {/* 可能触发并发渲染的组件 */}</ErrorBoundary>);
}
2. 全局错误监听兜底
对于 “未提交阶段” 的并发渲染错误(错误边界无法捕获),需依赖全局 error 事件监听:
// 在入口文件(如 index.js)中注册全局监听
window.addEventListener('error', (event) => {const error = event.error;console.error('全局捕获未提交的并发错误:', error);// 上报错误(可结合 React 特定标识判断是否为并发渲染错误)
});// 监听未处理的 Promise 错误(并发渲染中可能涉及异步操作)
window.addEventListener('unhandledrejection', (event) => {console.error('并发渲染中的未处理 Promise 错误:', event.reason);
});
3. 处理 useTransition 中的异常
使用 useTransition 触发的低优先级并发更新,若渲染中出错,需在组件内主动捕获:
import { useTransition, useState } from 'react';function ConcurrentComponent() {const [data, setData] = useState(null);const [isPending, startTransition] = useTransition();const fetchData = async () => {try {const res = await fetch('/large-data');const result = await res.json();// 用 startTransition 标记低优先级更新(可能触发并发渲染)startTransition(() => {setData(result); // 若 result 格式错误,渲染时可能抛错});} catch (err) {console.error('数据请求错误:', err); // 捕获请求阶段错误}};return (<div><button onClick={fetchData}>加载数据</button>{isPending ? '加载中...' : <DataRenderer data={data} />}</div>);
}// 子组件:渲染数据时可能抛错
function DataRenderer({ data }) {// 主动捕获渲染中的错误(针对并发渲染的准备阶段)try {if (!data) return null;// 假设 data 必须有 list 属性,否则抛错return <ul>{data.list.map(item => <li key={item.id}>{item.name}</li>)}</ul>;} catch (err) {console.error('DataRenderer 渲染错误(可能在并发准备阶段):', err);throw err; // 重新抛出,让错误边界或全局);throw err; // 重新抛出,让错误边界或全局监听捕获}
}
总结
并发模式异常:主要指在 React 18 并发渲染的 “准备阶段”(未提交到 DOM)抛出的错误,传统错误边界可能无法捕获。
捕获原则:
- 用错误边界处理 “已提交到 DOM” 的异常;
- 用全局 error 和 unhandledrejection 监听兜底 “未提交阶段” 的异常;
- 组件内主动 try/catch 处理已知可能出错的逻辑(如渲染动态数据)。
通过多层防护,可覆盖并发模式下的绝大多数异常场景,保障应用稳定性。