跳至主要内容
版本: v18.0.0

使用错误边界处理错误状态

您可能已经注意到,我们提到了使用 `usePreloadedQuery` 将渲染来自服务器获取的查询的数据,但我们没有详细说明如何在出现获取错误时渲染 UI 以显示错误。在本节中,我们将介绍这一点。

我们可以使用 错误边界 组件来捕获渲染期间发生的错误(由于网络错误或任何类型的错误),并在发生这种情况时渲染替代的错误 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 (/* ... */);
}
  • 本示例代码中的示例错误边界将为 `fallback` 提供一个 `retry` 函数,我们可以使用该函数来清除错误,重新加载查询,并使用新的查询引用重新渲染,我们可以将其传递给使用 `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` 时,我们将清除错误,并增加我们的 `fetchKey`,然后我们可以将其传递给 `useLazyLoadQuery`。这将使我们使用新的 `fetchKey` 重新渲染使用 `useLazyLoadQuery` 的组件,确保在对 `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 (/* ... */);
}
  • 本示例代码中的示例错误边界将为 `fallback` 提供一个 `retry` 函数,我们可以使用该函数来清除错误,并使用新的 `fetchKey` 重新渲染 `useLazyLoadQuery`。这将导致查询被重新评估和重新获取,`useLazyLoadQuery` 启动一个新的网络请求并挂起。

访问 GraphQL 响应中的错误

如果您希望访问应用程序中的错误信息以显示用户友好的消息,建议的方法是将错误信息建模并作为 GraphQL 模式的一部分公开。

例如,您可以在模式中公开一个字段,该字段返回预期的结果,或者在解析该字段时发生错误时返回 Error 对象(而不是返回 null)。

type Error {
# User friendly message
message: String!
}

type Foo {
bar: Result | Error
}

此页面是否有用?

通过以下方式帮助我们使网站更加完善 回答几个简短的问题.