Как обновлять данные

Вы можете обновлять данные в Next.js, используя Серверные функции (Server Functions) React. На этой странице объясняется, как создавать и вызывать серверные функции.

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

Серверная функция — это асинхронная функция, выполняемая на сервере. Серверные функции по своей природе асинхронны, так как вызываются клиентом через сетевой запрос. При вызове в рамках action они также называются Серверными действиями (Server Actions).

По соглашению, action — это асинхронная функция, передаваемая в startTransition. Серверные функции автоматически оборачиваются в startTransition, когда:

  • Передаются в <form> через проп action,
  • Передаются в <button> через проп formAction,
  • Передаются в useActionState.

Создание серверных функций

Серверную функцию можно определить с помощью директивы use server. Вы можете разместить директиву в начале асинхронной функции, чтобы пометить её как серверную, или в начале отдельного файла, чтобы пометить все экспортируемые функции в нём.

export async function createPost(formData: FormData) {
  'use server'
  const title = formData.get('title')
  const content = formData.get('content')

  // Обновление данных
  // Ревалидация кэша
}

export async function deletePost(formData: FormData) {
  'use server'
  const id = formData.get('id')

  // Обновление данных
  // Ревалидация кэша
}
export async function createPost(formData) {
  'use server'
  const title = formData.get('title')
  const content = formData.get('content')

  // Обновление данных
  // Ревалидация кэша
}

export async function deletePost(formData) {
  'use server'
  const id = formData.get('id')

  // Обновление данных
  // Ревалидация кэша
}

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

Серверные функции могут быть встроены в серверные компоненты, если добавить директиву "use server" в начало тела функции:

export default function Page() {
  // Серверное действие
  async function createPost(formData: FormData) {
    'use server'
    // ...
  }

  return <></>
}
export default function Page() {
  // Серверное действие
  async function createPost(formData) {
    'use server'
    // ...
  }

  return <></>
}

Клиентские компоненты

Невозможно определить серверные функции в клиентских компонентах. Однако их можно вызывать из клиентских компонентов, импортируя из файла с директивой "use server" в начале:

'use server'

export async function createPost() {}
'use server'

export async function createPost() {}
'use client'

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

export function Button() {
  return <button formAction={createPost}>Создать</button>
}
'use client'

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

export function Button() {
  return <button formAction={createPost}>Создать</button>
}

Вызов серверных функций

Существует два основных способа вызова серверной функции:

  1. Формы в серверных и клиентских компонентах
  2. Обработчики событий в клиентских компонентах

Формы

React расширяет HTML-элемент <form>, позволяя вызывать серверные функции через проп action.

При вызове в форме функция автоматически получает объект FormData. Данные можно извлечь с помощью методов FormData:

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

export function Form() {
  return (
    <form action={createPost}>
      <input type="text" name="title" />
      <input type="text" name="content" />
      <button type="submit">Создать</button>
    </form>
  )
}
import { createPost } from '@/app/actions'

export function Form() {
  return (
    <form action={createPost}>
      <input type="text" name="title" />
      <input type="text" name="content" />
      <button type="submit">Создать</button>
    </form>
  )
}
'use server'

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

  // Обновление данных
  // Ревалидация кэша
}
'use server'

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

  // Обновление данных
  // Ревалидация кэша
}

Полезно знать: При передаче в проп action серверные функции также называются Серверными действиями (Server Actions).

Обработчики событий

Серверную функцию можно вызвать в клиентском компоненте через обработчики событий, например onClick.

'use client'

import { incrementLike } from './actions'
import { useState } from 'react'

export default function LikeButton({ initialLikes }: { initialLikes: number }) {
  const [likes, setLikes] = useState(initialLikes)

  return (
    <>
      <p>Всего лайков: {likes}</p>
      <button
        onClick={async () => {
          const updatedLikes = await incrementLike()
          setLikes(updatedLikes)
        }}
      >
        Лайк
      </button>
    </>
  )
}
'use client'

import { incrementLike } from './actions'
import { useState } from 'react'

export default function LikeButton({ initialLikes }) {
  const [likes, setLikes] = useState(initialLikes)

  return (
    <>
      <p>Всего лайков: {likes}</p>
      <button
        onClick={async () => {
          const updatedLikes = await incrementLike()
          setLikes(updatedLikes)
        }}
      >
        Лайк
      </button>
    </>
  )
}

Примеры

Отображение состояния загрузки

Во время выполнения серверной функции можно показать индикатор загрузки с помощью хука React useActionState. Этот хук возвращает булево значение pending:

'use client'

import { useActionState, startTransition } from 'react'
import { createPost } from '@/app/actions'
import { LoadingSpinner } from '@/app/ui/loading-spinner'

export function Button() {
  const [state, action, pending] = useActionState(createPost, false)

  return (
    <button onClick={() => startTransition(action)}>
      {pending ? <LoadingSpinner /> : 'Создать пост'}
    </button>
  )
}
'use client'

import { useActionState, startTransition } from 'react'
import { createPost } from '@/app/actions'
import { LoadingSpinner } from '@/app/ui/loading-spinner'

export function Button() {
  const [state, action, pending] = useActionState(createPost, false)

  return (
    <button onClick={() => startTransition(action)}>
      {pending ? <LoadingSpinner /> : 'Создать пост'}
    </button>
  )
}

Ревалидация кэша

После обновления данных можно ревалидировать кэш Next.js и показать обновлённые данные, вызвав revalidatePath или revalidateTag внутри серверной функции:

import { revalidatePath } from 'next/cache'

export async function createPost(formData: FormData) {
  'use server'
  // Обновление данных
  // ...

  revalidatePath('/posts')
}
import { revalidatePath } from 'next/cache'

export async function createPost(formData) {
  'use server'
  // Обновление данных
  // ...
  revalidatePath('/posts')
}

Перенаправление

После обновления данных может потребоваться перенаправить пользователя на другую страницу. Это можно сделать, вызвав redirect внутри серверной функции:

'use server'

import { redirect } from 'next/navigation'

export async function createPost(formData: FormData) {
  // Обновление данных
  // ...

  redirect('/posts')
}
'use server'

import { redirect } from 'next/navigation'

export async function createPost(formData) {
  // Обновление данных
  // ...

  redirect('/posts')
}