reactjs 使用react-error-boundary的具有常量依赖数组的无限循环中的效果

pes8fvy9  于 2023-03-22  发布在  React
关注(0)|答案(1)|浏览(111)

在我下面的例子中,为什么devtools控制台中会出现无限循环的错误?看起来好像是resetErrorBoundary()导致useEffect再次触发,反之亦然,导致无限循环,但我不明白为什么useEffect即使在其依赖数组中有一个常量值也会继续运行。
This answer通过使用if语句显式地检查依赖数组值的更改来解决问题,但是useEffect不应该自动执行吗?我认为这样的if语句是多余的。
https://codesandbox.io/p/github/adamerose/error-boundary-example/main?file=%2Fsrc%2FApp.jsx

import { useEffect } from "react";
import { ErrorBoundary } from "react-error-boundary";

function ThisComponentWillError() {
  throw Error("SomeError");
}

function App() {
  return (
    <main>
      <StandardErrorBoundary>
        <ThisComponentWillError />
      </StandardErrorBoundary>
    </main>
  );
}

function ErrorFallback({ error, resetErrorBoundary }) {
  useEffect(() => {
    resetErrorBoundary();
  }, ["CONSTANT"]);

  return (
    <div>
      <p>Something went wrong:</p>
      <pre>{error.toString()}</pre>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  );
}

function StandardErrorBoundary({ children }) {
  return (
    <ErrorBoundary FallbackComponent={ErrorFallback}>{children}</ErrorBoundary>
  );
}

export default App;

注意-这只是一个最小的例子。我的实际项目在依赖项中有location.pathname,因为我想重置URL导航的错误,但我意识到无论我在依赖项数组中有什么,它都会无限循环。

pgvzfuti

pgvzfuti1#

一种调试方法是检查正在使用的库的源代码。幸运的是react-error-boundary只是一个组件,相对来说检查起来更容易。
你假设ErrorFallback组件在resetErrorBoundary被调用时被重新渲染。相反,它被完全重新挂载。一旦重新挂载,所有效果将再次运行,因为它就像是新的第一次调用函数。
下面是source code。我已经评论了不相关的部分:

const initialState: ErrorBoundaryState = {error: null}

class ErrorBoundary extends React.Component<
  React.PropsWithRef<React.PropsWithChildren<ErrorBoundaryProps>>,
  ErrorBoundaryState
> {
  static getDerivedStateFromError(error: Error) {
    return {error}
  }

  state = initialState
  resetErrorBoundary = (...args: Array<unknown>) => {
    this.props.onReset?.(...args)
    this.reset()
  }

  reset() {
    this.setState(initialState)
  }

  ....

  ....

  render() {
    const {error} = this.state

    const {fallbackRender, FallbackComponent, fallback} = this.props

    if (error !== null) {
      const props = {
        error,
        resetErrorBoundary: this.resetErrorBoundary,
      }
      if (React.isValidElement(fallback)) {
        return fallback
      } else if (typeof fallbackRender === 'function') {
        return fallbackRender(props)
      } else if (FallbackComponent) {
        return <FallbackComponent {...props} />
      } else {
        throw new Error(
          'react-error-boundary requires either a fallback, fallbackRender, or FallbackComponent prop',
        )
      }
    }

    return this.props.children
  }
}

因此,一旦调用了resetErrorBoundary。状态被重新初始化并变为{error:null}。现在,在这种情况下,错误边界 Package 代码的子代码将被呈现而不是回退。在上述情况下,子树再次抛出错误,因此状态变为{error:null}以外的值。再次调用ErrorBoundary的render方法,这次调用的是Fallback component is rendered because this time this。state.error不为null。因此完成循环,并继续。 PS:我能够通过运行一个带有空依赖项的useEffect`来发现该组件正在被重新挂载。

相关问题