Обработка ошибок

Ошибки можно разделить на две категории: ожидаемые ошибки и неперехваченные исключения. На этой странице вы узнаете, как обрабатывать эти ошибки в вашем приложении Next.js.

Обработка ожидаемых ошибок

Ожидаемые ошибки — это те, которые могут возникнуть в ходе нормальной работы приложения, например, ошибки валидации форм на стороне сервера или неудачные запросы. Эти ошибки должны обрабатываться явно и возвращаться клиенту.

Серверные функции

Для обработки ожидаемых ошибок в серверных функциях можно использовать хук useActionState.

Для таких ошибок избегайте использования блоков try/catch и выбрасывания ошибок. Вместо этого моделируйте ожидаемые ошибки как возвращаемые значения.

'use server'

export async function createPost(prevState: any, formData: FormData) {
  const title = formData.get('title')
  const content = formData.get('content')

  const res = await fetch('https://api.vercel.app/posts', {
    method: 'POST',
    body: { title, content },
  })
  const json = await res.json()

  if (!res.ok) {
    return { message: 'Failed to create post' }
  }
}
'use server'

export async function createPost(prevState, formData) {
  const title = formData.get('title')
  const content = formData.get('content')

  const res = await fetch('https://api.vercel.app/posts', {
    method: 'POST',
    body: { title, content },
  })
  const json = await res.json()

  if (!res.ok) {
    return { message: 'Failed to create post' }
  }
}

Вы можете передать ваше действие в хук useActionState и использовать возвращаемое состояние state для отображения сообщения об ошибке.

'use client'

import { useActionState } from 'react'
import { createPost } from '@/app/actions'

const initialState = {
  message: '',
}

export function Form() {
  const [state, formAction, pending] = useActionState(createPost, initialState)

  return (
    <form action={formAction}>
      <label htmlFor="title">Title</label>
      <input type="text" id="title" name="title" required />
      <label htmlFor="content">Content</label>
      <textarea id="content" name="content" required />
      {state?.message && <p aria-live="polite">{state.message}</p>}
      <button disabled={pending}>Create Post</button>
    </form>
  )
}
'use client'

import { useActionState } from 'react'
import { createPost } from '@/app/actions'

const initialState = {
  message: '',
}

export function Form() {
  const [state, formAction, pending] = useActionState(createPost, initialState)

  return (
    <form action={formAction}>
      <label htmlFor="title">Title</label>
      <input type="text" id="title" name="title" required />
      <label htmlFor="content">Content</label>
      <textarea id="content" name="content" required />
      {state?.message && <p aria-live="polite">{state.message}</p>}
      <button disabled={pending}>Create Post</button>
    </form>
  )
}

Серверные компоненты

При получении данных внутри серверного компонента вы можете использовать ответ для условного рендеринга сообщения об ошибке или выполнения редиректа.

export default async function Page() {
  const res = await fetch(`https://...`)
  const data = await res.json()

  if (!res.ok) {
    return 'There was an error.'
  }

  return '...'
}
export default async function Page() {
  const res = await fetch(`https://...`)
  const data = await res.json()

  if (!res.ok) {
    return 'There was an error.'
  }

  return '...'
}

Страница не найдена

Вы можете вызвать функцию notFound внутри сегмента маршрута и использовать файл not-found.js для отображения интерфейса 404.

import { getPostBySlug } from '@/lib/posts'

export default async function Page({ params }: { params: { slug: string } }) {
  const { slug } = await params
  const post = getPostBySlug(slug)

  if (!post) {
    notFound()
  }

  return <div>{post.title}</div>
}
import { getPostBySlug } from '@/lib/posts'

export default async function Page({ params }) {
  const { slug } = await params
  const post = getPostBySlug(slug)

  if (!post) {
    notFound()
  }

  return <div>{post.title}</div>
}
export default function NotFound() {
  return <div>404 - Page Not Found</div>
}
export default function NotFound() {
  return <div>404 - Page Not Found</div>
}

Обработка неперехваченных исключений

Неперехваченные исключения — это непредвиденные ошибки, указывающие на баги или проблемы, которые не должны возникать в нормальном потоке работы вашего приложения. Их следует обрабатывать путем выбрасывания ошибок, которые затем будут перехвачены границами ошибок.

Вложенные границы ошибок

Next.js использует границы ошибок для обработки неперехваченных исключений. Границы ошибок перехватывают ошибки в своих дочерних компонентах и отображают резервный интерфейс вместо дерева компонентов, в котором произошел сбой.

Создайте границу ошибок, добавив файл error.js внутрь сегмента маршрута и экспортируя React-компонент:

'use client' // Границы ошибок должны быть клиентскими компонентами

import { useEffect } from 'react'

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  useEffect(() => {
    // Логирование ошибки в сервис отчетов об ошибках
    console.error(error)
  }, [error])

  return (
    <div>
      <h2>Что-то пошло не так!</h2>
      <button
        onClick={
          // Попытка восстановить работу путем повторного рендеринга сегмента
          () => reset()
        }
      >
        Попробовать снова
      </button>
    </div>
  )
}
'use client' // Границы ошибок должны быть клиентскими компонентами

import { useEffect } from 'react'

export default function Error({ error, reset }) {
  useEffect(() => {
    // Логирование ошибки в сервис отчетов об ошибках
    console.error(error)
  }, [error])

  return (
    <div>
      <h2>Что-то пошло не так!</h2>
      <button
        onClick={
          // Попытка восстановить работу путем повторного рендеринга сегмента
          () => reset()
        }
      >
        Попробовать снова
      </button>
    </div>
  )
}

Ошибки будут всплывать до ближайшей родительской границы ошибок. Это позволяет реализовать детализированную обработку ошибок, размещая файлы error.tsx на разных уровнях иерархии маршрутов.

Nested Error Component Hierarchy

Глобальные ошибки

Хотя это встречается реже, вы можете обрабатывать ошибки в корневом макете с помощью файла global-error.js, расположенного в корневой директории приложения, даже при использовании интернационализации. Глобальный интерфейс ошибок должен определять собственные теги <html> и <body>, поскольку он заменяет корневой макет или шаблон при активации.

'use client' // Границы ошибок должны быть клиентскими компонентами

export default function GlobalError({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  return (
    // global-error должен включать теги html и body
    <html>
      <body>
        <h2>Что-то пошло не так!</h2>
        <button onClick={() => reset()}>Попробовать снова</button>
      </body>
    </html>
  )
}
'use client' // Границы ошибок должны быть клиентскими компонентами

export default function GlobalError({ error, reset }) {
  return (
    // global-error должен включать теги html и body
    <html>
      <body>
        <h2>Что-то пошло не так!</h2>
        <button onClick={() => reset()}>Попробовать снова</button>
      </body>
    </html>
  )
}