React 16のError Boundaryの運用方法
React 16のError Boundaryの運用方法:
React 16のUncaught Errorに対策して、ErrorBoudaryおよびErrorBoudaryHOC(HigherOrderComponent)を作成する
React 15: エラーが発生しても、UIが変わらない
React 16 以降: コンポーネントツリーが全部
ErrorBoundaryを使用する
Propとして
コンポネートごとにErrorBoudaryを使うと、ある時が面倒です。
-> Higher Order Componentを利用する
上記の問題は解決できます。また、HOCを使うと、view側は何も変更しないで、ErrorBoundaryも適用できる。
前述によると、エラーが発生したら、
そこを実装するために、単純にエラーが発生する時、
ErrorBoundaryを作成する
ErrorBoundaryを利用する
この記事について
React 16のUncaught Errorに対策して、ErrorBoudaryおよびErrorBoudaryHOC(HigherOrderComponent)を作成する
Uncaught Error
時のReactの挙動
React 15: エラーが発生しても、UIが変わらない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.
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> <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>
class ErrorBoundary extends React.Component { ... } function ErrorBoundaryHOC(Component){ return class ErrorBoundaryComponent extends React.Component { render(){ return ( <ErrorBoundary> <Component {...this.props} /> </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} } }
import ErrorBoundary from './ErrorBoundary' ErrorBoundary.setOptions({onError, ErrorComponent})
コメント
コメントを投稿