コード例
SuspenseSample.tsx
"use client";
import { FC, Suspense, use } from "react";
import { ErrorBoundary } from "../ErrorBoundary"
async function fetchWillError() {
throw new Error
};
const ErrorFC:FC<{errorPromise:Promise<any>}> = ({errorPromise}) => {
const data = use(errorPromise);
return (
<div>{JSON.stringify(data)}</div>
)
};
const FallbackFC:FC = () => {
return(
<div>Loading...</div>
)
};
export const PromiseErrorClient = () => {
const errorPromise = fetchWillError();
return (
<>
<ErrorBoundary>
<Suspense fallback={<FallbackFC />}>
<ErrorFC {...{errorPromise}}></ErrorFC>
</Suspense>
</ErrorBoundary>
</>
)
};
export default PromiseErrorClient
親コンポーネントで変数にPromiseを入れて、Suspenseの子コンポーネントにuse
APIで値を遅延読み込み。Promiseの失敗はErrorBoundaryコンポーネントで吸収する。
このコード例だと即時エラーをthrowしているが、ErrorBoundaryでwrapするだけでクラッシュを避けられる。
ErrorBoundary.tsx
import { Component, ReactNode } from 'react';
// Error Boudary Sample
export class ErrorBoundary extends Component<{ children: ReactNode }, { hasError: boolean; error: any }> {
constructor(props: { children: ReactNode }) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error: any) {
return { hasError: true, error };
}
componentDidCatch(error: any, errorInfo: any) {
console.error("Error caught in ErrorBoundary:", error, errorInfo);
}
render() {
if (this.state.hasError) {
return <div style={{ color: "red" }}>Error: {String(this.state.error.message || this.state.error)}</div>;
}
return this.props.children;
}
};
Bad Pattern
bad_pattern.tsx
/** bad pattern */
const ErrorFC:FC<{errorPromise:Promise<any>}> = ({errorPromise}) => {
try {
const data = use(errorPromise);
return <div>{JSON.stringify(data)}</div>
} catch(err) {
return <div>Error!</div>
}
};
use
をtry/catchで囲うなというエラーが出ます。