React 16のError Boundaryの運用方法

React 16のError Boundaryの運用方法:


この記事について

React 16のUncaught Errorに対策して、ErrorBoudaryおよびErrorBoudaryHOC(HigherOrderComponent)を作成する


Uncaught Error時のReactの挙動

React 15: エラーが発生しても、UIが変わらない


react15.gif


React 16 以降: コンポーネントツリーが全部unmountされる

As of React 16, errors that were not caught by any error boundary will result in unmounting of the whole React component tree.


react16.gif


React 16のErrorBoudaryの対応


ErrorBoudaryを作成して、componentDidCatchを使う


ErrorBoudary

  • 子孫コンポーネントの中にエラーが発生したら、catchできる

  • componentDidCatchというfunctionを使って、発生したエラーをハンドリングできる
  • ErrorBoudaryはいくつでも利用できる


componentDidCatch(error, errorInfo)

  • JavaScript catchみたい
  • error: react error
  • errorInfo: コンポーネントのエラーが発生したところがわかりやすい
The above error occurred in the <TodoItem> component: 
    in TodoItem (created by TodoApp) 
    in div (created by TodoList) 
    in TodoList (created by TodoApp) 
    in ErrorBoundary (created by TodoApp) 
    in div (created by TodoApp) 
    in TodoApp 
 
React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary. 


Code Demo jsfiddle

class ErrorBoundary extends React.Component { 
  constructor(props) { 
    super(props); 
    this.state = {error: null, errorInfo: null}; 
  } 
 
  componentDidCatch(error, errorInfo) { 
    // Catch errors in any components below and re-render with error message 
    this.setState({error, errorInfo}) 
    // You can also log error messages to an error reporting service here 
  } 
 
  render() { 
    if (this.state.errorInfo) { 
      // Error path 
      return ( 
        <div> 
          <h2>Something went wrong.</h2> 
          <details style={{whiteSpace: 'pre-wrap'}}> 
            {this.state.error && this.state.error.toString()} 
            <br /> 
            {this.state.errorInfo.componentStack} 
          </details> 
        </div> 
      ); 
    } 
    // Normally, just render children 
    return this.props.children; 
  }   
} 
 
ErrorBoundaryを使用する

<ErrorBoundary> 
 <TodoList/> 
 <AddTodo/> 
</ErrorBoundary> 


エラーハンドリングのカスタマイズ jsfiddle

PropとしてonError(発生したエラーをハンドリングするfuntion)とErrorComponent(Error message)を渡します。

const ErrorComponent = ({error, errorInfo}) => ( 
  <div> 
    <h2>Something went wrong.</h2> 
    <details style={{whiteSpace: 'pre-wrap'}}> 
      {error && error.toString()} 
      <br /> 
      {errorInfo.componentStack} 
    </details> 
  </div> 
) 
 
const onError = (error, errorInfo) => ( 
  console.error(error, errorInfo) 
) 
 
<ErrorBoundary ErrorComponent={ErrorComponent} onError={onError}> 
... 
</ErrorBoundary> 


Higher Order Component としてErrorBoudaryを利用する

コンポネートごとにErrorBoudaryを使うと、ある時が面倒です。

<ErrorBoundary> 
 <TodoList> 
  <ErrorBoundary> 
    <TodoItem/> 
  </ErrorBoundary> 
  <ErrorBoundary> 
    <TodoItem/> 
  </ErrorBoundary> 
 </TodoList> 
 <AddTodo/> 
</ErrorBoundary> 
 
-> Higher Order Componentを利用する

class ErrorBoundary extends React.Component { 
... 
} 
 
function ErrorBoundaryHOC(Component){ 
    return class ErrorBoundaryComponent extends React.Component { 
    render(){ 
      return ( 
        <ErrorBoundary> 
          <Component {...this.props} /> 
        </ErrorBoundary> 
      ) 
    } 
  } 
} 
上記の問題は解決できます。また、HOCを使うと、view側は何も変更しないで、ErrorBoundaryも適用できる。

const TodoItemHOC = ErrorBoundaryHOC(TodoItem) 
//Viewを変更したくない 
//const TodoItem = ErrorBoundaryHOC(require('./TodoItem') 
 
<ErrorBoundary> 
 <TodoList> 
  <TodoItemHOC/> 
  <TodoItemHOC/> 
 </TodoList> 
 <AddTodo/> 
</ErrorBoundary> 
 


お負け


React 15みたいにUIが変わりたくない

前述によると、エラーが発生したら、ErrorComponentをレンダリングですが、多分時々そこをやりたくないと思います。Userにとってもエラーメッセージを見たくないな。

そこを実装するために、単純にエラーが発生する時、ErrorComponentをレンダリングしないで、childrenをレンダリングします。

render() { 
    // Normally, just render children 
    return this.props.children; 
  }   
 


エラーハンドリングがデフォルトにして、全プロジェクトを使う

ErrorBoundaryを作成する

ErrorBoundary.defaults = { 
  ErrorComponent: null, 
  onError: null 
} 
 
ErrorBoundary.setOptions = function(opt) { 
  merge(ErrorBoundary.defaults, opt) 
} 
 
class ErrorBoundary extends React.Component { 
   constructor(props) { 
    super(props); 
    const {onError, ErrorComponent} = this.props 
    //propsが優先される 
    const opt = merge({}, ErrorBoundary.defaults, {onError, ErrorComponent}} 
    this.state = merge({error: null, errorInfo: null}, opt} 
  } 
} 
ErrorBoundaryを利用する

import ErrorBoundary from './ErrorBoundary' 
 
ErrorBoundary.setOptions({onError, ErrorComponent}) 

コメント

このブログの人気の投稿

投稿時間:2021-06-17 05:05:34 RSSフィード2021-06-17 05:00 分まとめ(1274件)

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2020-12-01 09:41:49 RSSフィード2020-12-01 09:00 分まとめ(69件)