35
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Next.jsAdvent Calendar 2018

Day 5

Next.jsのエラーハンドリング

Last updated at Posted at 2018-12-04

Next.jsを実践投入するには、エラーハンドリングまわりも考えないといけません。関心事は、サイト独自のエラーページデザインであったり、Sentryなどでのエラー通知だと思います。
まずはNext.jsが用意しているエラーページを見ていきましょう。

本記事は公式ドキュメントのエラーハンドリングの項目を一部翻訳しています。
https://github.com/zeit/next.js#custom-error-handling

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

Next.jsではデフォルトのエラーハンドリングページが用意されています。ステータスコードが404もしくは500のエラーをキャッチし、クライアントとサーバーサイドの両方でハンドリングされます。
もしデフォルトのエラーコンポーネントをオーバーライドしたいのなら、_error.jsというファイルをpagesフォルダーの中に定義しましょう:

pages/_error.js
import React from 'react'

export default class Error extends React.Component {
  static getInitialProps({ res, err }) {
    const statusCode = res ? res.statusCode : err ? err.statusCode : null;
    return { statusCode }
  }

  render() {
    return (
      <p>
        {`${this.props.statusCode}エラーが起こりました。`}
      </p>
    )
  }
}

エラーハンドリングは、独自の_errorコンポーネントをインポートして行います。

pages/index.js
import React from 'react'
import Error from './_error'
import fetch from 'isomorphic-unfetch'

export default class Page extends React.Component {
  static async getInitialProps() {
    const res = await fetch('https://api.github.com/repos/zeit/next.js')
    const statusCode = res.statusCode > 200 ? res.statusCode : false
    const json = await res.json()

    return { statusCode, stars: json.stargazers_count }
  }

  render() {
    if (this.props.statusCode) {
      return <Error statusCode={this.props.statusCode} />
    }

    return (
      <div>
        Next stars: {this.props.stars}
      </div>
    )
  }
}

ここで少し注意したいのは、_error.jsgetInitialProps()はサーバから直接レンダリングされた場合のみ機能します。例えば存在しないページhttp://localhost:3000/hogefugaにアクセスしたときはgetInitialProps()を通過し、statusCodeを取得して404ページがレンダリングされます。逆に、pages/index.jsの例のように、ページの処理途中にエラーが起こった場合は_error.jsは普通のReactコンポーネントと同じ扱いで呼び出されるため、getInitialProps()は実行されません。

また、Next.jsが用意するエラーページを再利用したい場合は'./_error'ではなく'next/error'から同様にインポートしましょう。

Sentryの導入

前日の_app.jsについての記事でも触れましたが、<App>コンポーネントは全てのページにおいて、初期化時に利用されます。
ですので、このコンポーネントのライフサイクルcomponentDidCatchでエラーハンドリングを行えば、全エラーの捕捉が可能です。

公式の例より

pages/_app.js
import App from 'next/app'
import * as Sentry from '@sentry/browser'

const SENTRY_PUBLIC_DSN = ''

export default class MyApp extends App {
  constructor (...args) {
    super(...args)
    Sentry.init({dsn: SENTRY_PUBLIC_DSN})
  }

  componentDidCatch (error, errorInfo) {
    Sentry.configureScope(scope => {
      Object.keys(errorInfo).forEach(key => {
        scope.setExtra(key, errorInfo[key])
      })
    })
    Sentry.captureException(error)

    // This is needed to render errors correctly in development / production
    super.componentDidCatch(error, errorInfo)
  }
}

しかし、上記例の場合だと実はサーバーサイドのエラーもクライアントと同じように拾ってしまいます。'@sentry/browser'はサーバーサイドでも一応動くようです。
一旦入れる場合にはよいかもしれませんが、サーバーとクライアントを区別してエラーハンドリングを行う方法を、まさにIssueで議論が行われています。注視していきたいです。
Improve with-sentry example #5727

35
29
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
35
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?