メインコンテンツにスキップ
バージョン: v18.0.0

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
}

このページは役に立ちましたか?

いくつかの簡単な質問に 答えて、サイトをさらに改善するお手伝いをしてください。.