ErrorBoundariesによるエラー状態
ご存じのとおり、usePreloadedQuery を使用すると、サーバーからフェッチされた(またはフェッチ中)クエリのデータがレンダリングされると言いました。ただし、フェッチ中にエラーが発生した場合にエラーを表示する UI をレンダリングする方法については詳しく説明しませんでした。このセクションではその点について説明します。
Error Boundary コンポーネントを使用して、レンダリング中に発生するエラー(ネットワーク エラーやその他の種類のエラーが原因)をキャッチし、その場合に代替エラー UI をレンダリングできます。その仕組みは Suspense の仕組みと似ており、コンポーネントツリーをエラーバウンダリでラップして、エラーが発生したときの対処方法(フォールバック UI をレンダリングするなど)を指定できます。
エラーバウンダリ は単に静的な getDerivedStateFromError メソッドを実装するコンポーネントです
const React = require('React');
type State = {error: ?Error};
class ErrorBoundary extends React.Component<Props, State> {
static getDerivedStateFromError(error): State {
// Set some state derived from the caught error
return {error: error};
}
}
/**
* App.react.js
*/
const ErrorBoundary = require('ErrorBoundary');
const React = require('React');
const MainContent = require('./MainContent.react');
const SecondaryContent = require('./SecondaryContent.react');
function App() {
return (
// Render an ErrorSection if an error occurs within
// MainContent or Secondary Content
<ErrorBoundary fallback={error => <ErrorUI error={error} />}>
<MainContent />
<SecondaryContent />
</ErrorBoundary>
);
}
- エラーバウンダリを使用してサブツリーをラップし、そのサブツリー内でエラーが発生したときに別の UI を表示できます。エラーが発生すると、指定された
fallbackが、バウンダリ内のコンテンツの代わりにレンダリングされます。 - エラー UI をレンダリングする粒度を制御し、さまざまなレベルのコンポーネントにエラーバウンダリをラップすることもできます。この例では、
MainContentまたはSecondaryContent内でエラーが発生した場合、アプリのすべてのコンテンツの代わりにErrorSectionをレンダリングします。
エラー後の再試行
useQueryLoader / loadQuery を使用する場合
useQueryLoader/loadQuery を使用してクエリをフェッチする場合、エラー発生後に再試行するには、loadQuery をもう一度呼び出して新しいクエリの参照を usePreloadedQuery に渡すことができます
/**
* ErrorBoundaryWithRetry.react.js
*/
const React = require('React');
// NOTE: This is NOT actual production code;
// it is only used to illustrate example
class ErrorBoundaryWithRetry extends React.Component<Props, State> {
state = {error: null};
static getDerivedStateFromError(error): State {
return {error: error};
}
_retry = () => {
// This ends up calling loadQuery again to get and render
// a new query reference
this.props.onRetry();
this.setState({
// Clear the error
error: null,
});
}
render() {
const {children, fallback} = this.props;
const {error} = this.state;
if (error) {
if (typeof fallback === 'function') {
return fallback({error, retry: this._retry});
}
return fallback;
}
return children;
}
}
- エラーが発生した場合、提供された
fallbackを描画します。 retryが呼び出されると、エラーがクリアされ、loadQueryが再び呼び出されます。これによりクエリが再びフェッチされ、新しいクエリ参照が提供され、usePreloadedQueryに渡すことができます。
/**
* App.react.js
*/
const ErrorBoundaryWithRetry = require('ErrorBoundaryWithRetry');
const React = require('React');
const MainContent = require('./MainContent.react');
const query = require('__generated__/MainContentQuery.graphql');
// NOTE: This is NOT actual production code;
// it is only used to illustrate example
function App(props) {
// E.g., initialQueryRef provided by router
const [queryRef, loadQuery] = useQueryLoader(query, props.initialQueryRef);
return (
<ErrorBoundaryWithRetry
// On retry we call loadQuery again, which will update
// the value of queryRef from useQueryLoader with a new
// fresh query reference
onRetry={() => loadQuery(/* ... */)}
fallback={({error, retry}) =>
<>
<ErrorUI error={error} />
{/* Render a button to retry; this will attempt to re-render the
content inside the boundary, i.e. the query component */}
<Button onPress={retry}>Retry</Button>
</>
}>
{/* The value of queryRef will be updated after calling
loadQuery again */}
<MainContent queryRef={queryRef} />
</ErrorBoundaryWithRetry>
);
}
/**
* MainContent.react.js
*/
function MainContent(props) {
const data = usePreloadedQuery(
graphql`...`,
props.queryRef
);
return (/* ... */);
}
- このサンプルコードのエラー境界は、
retry関数をfallbackに提供します。これを使用してエラーをクリアし、クエリを再読み込みし、usePreloadedQueryを使用するコンポーネントに渡すことができる新しいクエリ参照で再描画できます。そのコンポーネントは新しいクエリ参照を使用し、必要に応じて新しいネットワーク要求でサスペンドします。
useLazyLoadQueryを使用する場合
useLazyLoadQueryを使用してクエリをフェッチする場合、エラーが発生した後に再試行するには、新しいfetchKeyを渡すことによってクエリコンポーネントを再マウントおよび再評価できます。
/**
* ErrorBoundaryWithRetry.react.js
*/
const React = require('React');
// NOTE: This is NOT actual production code;
// it is only used to illustrate example
class ErrorBoundaryWithRetry extends React.Component<Props, State> {
state = {error: null, fetchKey: 0};
static getDerivedStateFromError(error): State {
return {error: error, fetchKey: 0};
}
_retry = () => {
this.setState(prev => ({
// Clear the error
error: null,
// Increment and set a new fetchKey in order
// to trigger a re-evaluation and refetching
// of the query using useLazyLoadQuery
fetchKey: prev.fetchKey + 1,
}));
}
render() {
const {children, fallback} = this.props;
const {error, fetchKey} = this.state;
if (error) {
if (typeof fallback === 'function') {
return fallback({error, retry: this._retry});
}
return fallback;
}
return children({fetchKey});
}
}
- エラーが発生した場合、提供された
fallbackを描画します。 retryが呼び出されると、エラーがクリアされ、useLazyLoadQueryに渡すことができるfetchKeyが増分されます。これにより、useLazyLoadQueryを使用するコンポーネントが新しいfetchKeyで再描画され、useLazyLoadQueryへの新しい呼び出し時にクエリが再フェッチされるようにします。
/**
* App.react.js
*/
const ErrorBoundaryWithRetry = require('ErrorBoundaryWithRetry');
const React = require('React');
const MainContent = require('./MainContent.react');
// NOTE: This is NOT actual production code;
// it is only used to illustrate example
function App() {
return (
<ErrorBoundaryWithRetry
fallback={({error, retry}) =>
<>
<ErrorUI error={error} />
{/* Render a button to retry; this will attempt to re-render the
content inside the boundary, i.e. the query component */}
<Button onPress={retry}>Retry</Button>
</>
}>
{({fetchKey}) => {
// If we have retried, use the new `retryQueryRef` provided
// by the Error Boundary
return <MainContent fetchKey={fetchKey} />;
}}
</ErrorBoundaryWithRetry>
);
}
/**
* MainContent.react.js
*/
function MainContent(props) {
const data = useLazyLoadQuery(
graphql`...`,
variables,
{fetchKey: props.fetchKey}
);
return (/* ... */);
}
- このサンプルコードのエラー境界は、エラーをクリアし、
fetchKeyを新しくしてuseLazyLoadQueryを再描画するために使用できるretry関数をfallbackに提供します。これにより、クエリが再評価され、再フェッチされ、useLazyLoadQueryが新しいネットワーク要求を開始してサスペンドされます。
GraphQLレスポンスのエラーにアクセスする
アプリケーションでエラー情報にアクセスしてわかりやすいメッセージを表示する場合は、GraphQLスキーマの一部としてエラー情報をモデル化して公開することをお勧めします。
たとえば、予想される結果を返すフィールドをスキーマに公開するか、そのフィールドを解決中にエラーが発生した場合に(nullを返すのではなく)エラーオブジェクトを公開できます。
type Error {
# User friendly message
message: String!
}
type Foo {
bar: Result | Error
}
このページは役に立ちましたか?
いくつかの簡単な質問に 答えて、サイトをさらに改善するお手伝いをしてください。.