web-dev-qa-db-ja.com

Reactエラー境界を使用してブラウザナビゲーションが壊れている

React 16コードベースでエラーがスローされると、トップレベルのエラー境界によってキャッチされます。これが発生すると、ErrorBoundaryコンポーネントはエラーページを適切にレンダリングします。

ErrorBoundaryが置かれている場所

   return (
     <Provider store={configureStore()}>
       <ErrorBoundary>
         <Router history={browserHistory}>{routes}</Router>
       </ErrorBoundary>
     </Provider>
   )

ただし、ブラウザの戻るボタン(ワンクリック)を使用して戻ると、アドレスのURLは変更されますが、ページは更新されません。

エラー境界をコンポーネントツリーの下にシフトしようとしましたが、この問題は解決しません。

この問題がどこにあるかについての手がかりはありますか?

18

作戦はおそらく今までに解決策を見つけましたが、この問題を抱えている他の人のために、なぜそれが起こっていると思うのか、そしてそれを解決するために何ができるのかを説明します。

これは、履歴が変更されていても、エラーメッセージをレンダリングするErrorBoundaryの条件付きレンダリングが原因で発生している可能性があります。

上には示されていませんが、ErrorBoundaryのrenderメソッドはおそらく次のようになっています。

render() {
  if (this.state.hasError) {
    return <h1>An error has occurred.</h1>
  }

  return this.props.children;
}

componentDidCatchライフサイクルメソッドでhasErrorが設定されている場所。

ErrorBoundaryの状態が設定されると、状態が変化するまで常にエラーメッセージが表示されます(上記の例ではhasErrorがfalseになります)。履歴が変更されても、子コンポーネント(この場合はルーターコンポーネント)はレンダリングされません。

これを解決するには、errorBoundaryのエクスポートをラップして、小道具を介して履歴にアクセスできるようにすることで、react-router withRouter 高次コンポーネントを利用します。

export default withRouter(ErrorBoundary);

ErrorBoundaryコンストラクターで、小道具から履歴を取得し、 history.listen を使用して現在の場所への変更をリッスンするハンドラーを設定します。コンポーネントがエラー状態の場合に場所が変更されると(戻るボタンがクリックされるなど)、それはクリアされ、子を再度レンダリングできるようになります。

const { history } = this.props;

history.listen((location, action) => {
  if (this.state.hasError) {
    this.setState({
      hasError: false,
    });
  }
});
35
jdavies

上記のjdaviesの回答に追加するには、履歴リスナーをcomponentDidMountまたはuseEffectに登録してください(依存関係がないことを示すために[]を使用)。nregistercomponentWillUnmountまたはuseEffect returnステートメントでそれを実行しないと、マウントされていないコンポーネントでsetStateが呼び出される問題が発生する可能性があります。

例:

  componentDidMount() {
    this.unlisten = this.props.history.listen((location, action) => {
      if (this.state.hasError) {
        this.setState({ hasError: false });
      }
    });
  }

  componentWillUnmount() {
    this.unlisten();
  }
2
eyeballTickler

エラーが検出されたら、ErrorBoundaryのエラーステータスをリセットする必要があります。以下のようなもの:

shouldComponentUpdate(nextProps, nextState) {
    if (this.state.hasError && nextState.hasError === false) {
      return false;
    }
    return true;
  }

render() {
    if (this.state.hasError) {
      this.setState({ hasError: false });
     // return fallback component to show on error
    }
    return this.props.children;
  }
}
1
geekprogrammer

tl; drエラー境界のあるエラーが予想されるコンポーネントをラップしますがツリー全体ではありません

最初にwithRouterを使用して@jdaviesの回答を試しましたが、その後、私のユースケースに適した解決策を見つけました: React-TeamのDanは、エラー境界のあるHOCを使用せず、戦略的な場所で使用するようアドバイスしました

そのTwitterスレッドでは、賛否両論が議論されており、Danはどちらに進むべきかを開いたままにしましたが、彼の考えは説得力があります。

だから私がしたことは、ツリー全体ではなく、エラーが予想される戦略的な場所をラップすることでした。以前よりも表現力豊かで具体的なエラーページをスローできるため、これをユースケースに使用します(問題が発生しましたvs認証エラーが発生しました)。

0
dema