【问题标题】:With React 18 and suspense, how to display specific errors, not just fallback in ErrorBoundary使用 React 18 和 suspense,如何显示特定错误,而不仅仅是在 ErrorBoundary 中回退
【发布时间】:2022-01-10 15:29:05
【问题描述】:

我有一个像这样结构的组件,我的 ErrorBoundary 包装了我的 Suspense 元素。

function App() {
  return (
    <ErrorBoundary fallback={<h2>Could not fetch cities.</h2>}>
      <Suspense fallback={<div>Loading..</div>}>
        <MyList />
      </Suspense>
    </ErrorBoundary>
  );
}

MyList 组件包括一个SWR 数据获取钩子,如下所示:

const { data } = useSwr(`/api/mydata`, fetcher, {
      suspense: true,
    });

我的 fetcher 方法抛出如下错误:

  const rsp = await fetch(url);
  if (rsp.ok) {
    return await rsp.json();
  } else {
    const MyError = function (message, status) {
      this.message = `${message} from url ${url} status code:${status}`;
      this.status = status;
    };
    throw new MyError(rsp.statusText, rsp.status);
  }
}

当错误发生时,我不知道如何让我的 UI 显示抛出的值(即 MyError 类中的内容)

【问题讨论】:

  • 你能用你的 ErrorBoundary 类更新代码吗?
  • 可能最棘手的部分是如何触发要检查的错误,特别是因为代码使用async\await。检查我的答案,让我知道这是否适合您。
  • 我认为下面的答案不能解决我的问题(我的问题不够清楚)。我想在我在 m App 类中定义的后备组件中包含实际错误。我会更新问题
  • 嗯,好的,请包含一些对ErrorBoundary的引用,所以如果是从库中导入的,那么实现代码或一些导入。
  • 您会想使用github.com/bvaughn/react-error-boundary#readme 和我的回答stackoverflow.com/questions/70621393/… 可能会对您有所帮助。如果没有您对ErrorBoundary 的实施,很难看出您可能做错了什么。并且,请阅读kentcdodds.com/blog/…

标签: reactjs swr react-suspense


【解决方案1】:

我不确定您是否正在使用某个库与名为 ErrorBoundary 的组件一起使用,但您自己编写的方式类似于以下内容:

class MyErrorBoundary extends React.Component {
  state = { error: null }

  static getDerivedStateFromError(error) {
    return { error };
  }

  render() {
    if (this.state.error) {
      // render whatever you like for the error case
      return <h2>{this.state.error.message}</h2>
    } else {
      return this.props.children
    }
  }
}

【讨论】:

    【解决方案2】:

    您应该在 ErrorBoundary 组件中使用此生命周期:

    https://reactjs.org/docs/react-component.html#componentdidcatch

    类似(改编自文档,解释如何以被ErrorBoundary拦截的方式触发错误):

    // In ErrorBoundary
    
    componentDidCatch(error, errorInfo) {
      this.setState({
        error: error,
        errorInfo: errorInfo
      });
    }
    
    // In MyList
    
    buggyMethod() {
      fetch("something-wrong").then(error => this.setState({ error }));
    }
    
    render() {
     if(this.state.error){
       throw this.state.error;
     }
     return <span>Something cool!</span>;
    }
    

    注意

    看起来有点连线,但与官方文档“现场演示”部分中使用的技术相同:

    https://reactjs.org/docs/error-boundaries.html#live-demo

    【讨论】:

      【解决方案3】:

      根据docs,您可以访问componentDidCatch 中的errorerrorInfo。您可以将其设置为stateErrorBoundary。您可以做的是使用第三方库(react-json-tree)很好地查看错误。

      import JSONTree from 'react-json-tree';
      
      class ErrorBoundary extends React.Component {
        constructor(props) {
          super(props);
          this.state = { hasError: false, error: null, errorInfo: null };
        }
      
        static getDerivedStateFromError(error) {
          // Update state so the next render will show the fallback UI.
          return { hasError: true };
        }
      
        componentDidCatch(error, errorInfo) {
          // You can also log the error to an error reporting service
          this.setState({ error, errorInfo });
        }
      
        render() {
          if (this.state.hasError) {
            // You can render any custom fallback UI
            return <JSONTree data={this.state.error}/>;
          }
      
          return this.props.children; 
        }
      }
      

      【讨论】:

        【解决方案4】:

        这是我一直在寻找的答案:

        fetcher.js

        export async function fetcher(url) {
          const rsp = await fetch(url);
          if (rsp.ok) {
            return await rsp.json();
          } else {
            const MyError = function (message, status) {
              this.message = `${message} from url ${url} status code:${status}`;
              this.status = status;
            };
            throw new MyError(rsp.statusText, rsp.status);
          }
        }
        

        ErrorBoundary.js

        class ErrorBoundary extends React.Component {
          constructor(props) {
            super(props);
            this.state = { hasError: false };
          }
        
          static getDerivedStateFromError(error) {
            // Update state so the next render will show the fallback UI.
            return { hasError: true, message: error?.message, status: error?.status };
          }
        
          render() {
            function addExtraProps(Component, extraProps) {
              return <Component.type {...Component.props} {...extraProps} />;
            }
        
            if (this.state.hasError) {
              return addExtraProps(this.props.fallback, {
                errorMessage: this.state.message,
                errorStatus: this.state.status,
              });
            }
            return this.props.children;
          }
        }
        

        然后用法是这样的:

        function CityLayout(props) {
          const { setSelectedCityId } = useContext(CityContext);
          return (
            <>
              <CityListMaxDDL />
              <CityList displayCount={5} />
              <CityDetail cityId={setSelectedCityId} />
            </>
          );
        }
        
        function App() {
          function MyErrorBoundaryFallback({ errorMessage, errorStatus }) {
            return (
              <div className="container">
                <h1>Error</h1>
                <div className="row">
                  Error Status: <b>{errorStatus}</b>
                </div>
                <div className="row">
                  ErrorMessage: <b>{errorMessage}</b>
                </div>
              </div>
            );
          }
        
          return (
            <ErrorBoundary fallback={<MyErrorBoundaryFallback />}>
              <Suspense fallback={<div>Loading..</div>}>
                <div className="container">
                  <CityProvider>
                    <CityLayout />
                  </CityProvider>
                </div>
              </Suspense>
            </ErrorBoundary>
          );
        

        【讨论】:

        • 这没有意义。 MyErrorBoundaryFallbackerrorMessageerrorStatus 作为道具,但这些值没有填写在您的回报中。
        猜你喜欢
        • 2020-03-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-25
        相关资源
        最近更新 更多