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
}
このページは役に立ちましたか?
いくつかの簡単な質問に 答えて、サイトをさらに改善するお手伝いをしてください。.