使用 React 18 和 suspense,如何显示特定的错误,而不仅仅是在 ErrorBoundary 中回退
With React 18 and suspense, how to display specific errors, not just fallback in ErrorBoundary
我有一个这样结构的组件,我的 ErrorBoundary
包装了我的 Suspense
元素。
function App() {
return (
<ErrorBoundary fallback={<h2>Could not fetch cities.</h2>}>
<Suspense fallback={<div>Loading..</div>}>
<MyList />
</Suspense>
</ErrorBoundary>
);
}
MyList
组件包含一个 SWR
数据获取挂钩,如下所示:
const { data } = useSwr(`/api/mydata`, fetcher, {
suspense: true,
});
我的 fetcher 方法抛出如下错误:
const rsp = await fetch(url);
if (rsp.ok) {
return await rsp.json();
} else {
const MyError = function (message, status) {
this.message = `${message} from url ${url} status code:${status}`;
this.status = status;
};
throw new MyError(rsp.statusText, rsp.status);
}
}
当错误发生时,我不知道如何让我的 UI 显示抛出的值(即 MyError class 中的内容)
我不确定是否有一些库与名为 ErrorBoundary 的组件一起使用,但您可以编写自己的库来执行此操作,如下所示:
class MyErrorBoundary extends React.Component {
state = { error: null }
static getDerivedStateFromError(error) {
return { error };
}
render() {
if (this.state.error) {
// render whatever you like for the error case
return <h2>{this.state.error.message}</h2>
} else {
return this.props.children
}
}
}
按照docs, you have access to error
and errorInfo
in componentDidCatch
. You can set it to state
of the ErrorBoundary
. What you can do is use a third-party library (react-json-tree)查看错误就好了
import JSONTree from 'react-json-tree';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null, errorInfo: null };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
this.setState({ error, errorInfo });
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <JSONTree data={this.state.error}/>;
}
return this.props.children;
}
}
您应该在 ErrorBoundary
组件中使用此生命周期:
https://reactjs.org/docs/react-component.html#componentdidcatch
类似(改编自文档,解释如何以被ErrorBoundary
拦截的方式触发错误:
// In ErrorBoundary
componentDidCatch(error, errorInfo) {
this.setState({
error: error,
errorInfo: errorInfo
});
}
// In MyList
buggyMethod() {
fetch("something-wrong").then(error => this.setState({ error }));
}
render() {
if(this.state.error){
throw this.state.error;
}
return <span>Something cool!</span>;
}
备注
似乎有点连线,但与官方文档“现场演示”部分中使用的技术相同:
这是我要找的答案:
fetcher.js
export async function fetcher(url) {
const rsp = await fetch(url);
if (rsp.ok) {
return await rsp.json();
} else {
const MyError = function (message, status) {
this.message = `${message} from url ${url} status code:${status}`;
this.status = status;
};
throw new MyError(rsp.statusText, rsp.status);
}
}
ErrorBoundary.js
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true, message: error?.message, status: error?.status };
}
render() {
function addExtraProps(Component, extraProps) {
return <Component.type {...Component.props} {...extraProps} />;
}
if (this.state.hasError) {
return addExtraProps(this.props.fallback, {
errorMessage: this.state.message,
errorStatus: this.state.status,
});
}
return this.props.children;
}
}
然后用法是这样的:
function CityLayout(props) {
const { setSelectedCityId } = useContext(CityContext);
return (
<>
<CityListMaxDDL />
<CityList displayCount={5} />
<CityDetail cityId={setSelectedCityId} />
</>
);
}
function App() {
function MyErrorBoundaryFallback({ errorMessage, errorStatus }) {
return (
<div className="container">
<h1>Error</h1>
<div className="row">
Error Status: <b>{errorStatus}</b>
</div>
<div className="row">
ErrorMessage: <b>{errorMessage}</b>
</div>
</div>
);
}
return (
<ErrorBoundary fallback={<MyErrorBoundaryFallback />}>
<Suspense fallback={<div>Loading..</div>}>
<div className="container">
<CityProvider>
<CityLayout />
</CityProvider>
</div>
</Suspense>
</ErrorBoundary>
);
我有一个这样结构的组件,我的 ErrorBoundary
包装了我的 Suspense
元素。
function App() {
return (
<ErrorBoundary fallback={<h2>Could not fetch cities.</h2>}>
<Suspense fallback={<div>Loading..</div>}>
<MyList />
</Suspense>
</ErrorBoundary>
);
}
MyList
组件包含一个 SWR
数据获取挂钩,如下所示:
const { data } = useSwr(`/api/mydata`, fetcher, {
suspense: true,
});
我的 fetcher 方法抛出如下错误:
const rsp = await fetch(url);
if (rsp.ok) {
return await rsp.json();
} else {
const MyError = function (message, status) {
this.message = `${message} from url ${url} status code:${status}`;
this.status = status;
};
throw new MyError(rsp.statusText, rsp.status);
}
}
当错误发生时,我不知道如何让我的 UI 显示抛出的值(即 MyError class 中的内容)
我不确定是否有一些库与名为 ErrorBoundary 的组件一起使用,但您可以编写自己的库来执行此操作,如下所示:
class MyErrorBoundary extends React.Component {
state = { error: null }
static getDerivedStateFromError(error) {
return { error };
}
render() {
if (this.state.error) {
// render whatever you like for the error case
return <h2>{this.state.error.message}</h2>
} else {
return this.props.children
}
}
}
按照docs, you have access to error
and errorInfo
in componentDidCatch
. You can set it to state
of the ErrorBoundary
. What you can do is use a third-party library (react-json-tree)查看错误就好了
import JSONTree from 'react-json-tree';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null, errorInfo: null };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
this.setState({ error, errorInfo });
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <JSONTree data={this.state.error}/>;
}
return this.props.children;
}
}
您应该在 ErrorBoundary
组件中使用此生命周期:
https://reactjs.org/docs/react-component.html#componentdidcatch
类似(改编自文档,解释如何以被ErrorBoundary
拦截的方式触发错误:
// In ErrorBoundary
componentDidCatch(error, errorInfo) {
this.setState({
error: error,
errorInfo: errorInfo
});
}
// In MyList
buggyMethod() {
fetch("something-wrong").then(error => this.setState({ error }));
}
render() {
if(this.state.error){
throw this.state.error;
}
return <span>Something cool!</span>;
}
备注
似乎有点连线,但与官方文档“现场演示”部分中使用的技术相同:
这是我要找的答案:
fetcher.js
export async function fetcher(url) {
const rsp = await fetch(url);
if (rsp.ok) {
return await rsp.json();
} else {
const MyError = function (message, status) {
this.message = `${message} from url ${url} status code:${status}`;
this.status = status;
};
throw new MyError(rsp.statusText, rsp.status);
}
}
ErrorBoundary.js
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true, message: error?.message, status: error?.status };
}
render() {
function addExtraProps(Component, extraProps) {
return <Component.type {...Component.props} {...extraProps} />;
}
if (this.state.hasError) {
return addExtraProps(this.props.fallback, {
errorMessage: this.state.message,
errorStatus: this.state.status,
});
}
return this.props.children;
}
}
然后用法是这样的:
function CityLayout(props) {
const { setSelectedCityId } = useContext(CityContext);
return (
<>
<CityListMaxDDL />
<CityList displayCount={5} />
<CityDetail cityId={setSelectedCityId} />
</>
);
}
function App() {
function MyErrorBoundaryFallback({ errorMessage, errorStatus }) {
return (
<div className="container">
<h1>Error</h1>
<div className="row">
Error Status: <b>{errorStatus}</b>
</div>
<div className="row">
ErrorMessage: <b>{errorMessage}</b>
</div>
</div>
);
}
return (
<ErrorBoundary fallback={<MyErrorBoundaryFallback />}>
<Suspense fallback={<div>Loading..</div>}>
<div className="container">
<CityProvider>
<CityLayout />
</CityProvider>
</div>
</Suspense>
</ErrorBoundary>
);