Асинхронные изменения данных и их обработка являются необходимыми задачами в современных веб-приложениях. Вы можете захотеть выполнять самостоятельную асинхронную функцию на сервере для выполнения таких задач, как сохранение данных в базе данных, отправка电子邮件, загрузка PDF-файлов, обработка изображений и т. д.
Next.js обеспечивает нам инструмент Server Actions
, который является асинхронной функцией, выполняемой на сервере. Мы можем использовать серверные действия для мутаций данных на сервере, но серверные действия могут вызываться из обеих частей — серверных и клиентских компонентов.
Серверные действия — это отличный способ обрабатывать отправку форм, выполняя действие, когда данные формы отправлены. В этой статье мы посмотрим на практический пример обработки дополнительных аргументов в серверных действиях Next.js.
Если у вас интересно изучить серверные действия Next.js с дизайн-паттернами и созданием проектов, я создал для вас краткий курс, который вы можете найти здесь.
Также статья доступна в виде видеоурока здесь:
Таблица содержания
为什么您需要传递附加参数?
当我们在表单提交时执行服务器动作,服务器动作会自动获取表单数据。例如,看看下面的表单:
<form className="p-4 flex" action={updateUser}>
<Input className="w-1/2 mx-2" type="text" name="name" />
<Button type="submit">Update User Name</Button>
</form>
在此处,当表单提交时,我们将执行名为 updateUser
的服务器动作。updateUser
函数将接收提交表单数据作为参数,可用于提取表单字段值。
正如您在下面的代码片段中看到的那样,updateUser
函数获得一个 formData
作为参数,我们可以从中提取 name
字段的值。
"use server"
export async function updateUser(formData) {
const name = formData.get('name');
console.log(name);
}
尽管这种模式覆盖了大多数基本用例,但您可能需要以编程方式向服务器操作传递额外的参数。这些参数不是表单或表单数据或用户输入数据的一部分。它们是传递给您的服务器操作的编程值。
为了理解这一点,请查看下面的服务器操作代码片段。这是我们之前看到过的同一个服务器操作,但我们传递了一个额外的userId
参数以及常规的formData
参数。
"use server"
export async function updateUser(userId, formData) {
const name = formData.get('name');
console.log(userId);
console.log(name);
}
userId
的值是应用程序内部的某些东西 – 您不会要求用户将该值作为表单提交的一部分提交。相反,您可能需要以编程方式将它们传递给您的服务器操作以执行进一步的计算。
好,这就是我们要讨论的使用案例。既然我们已经理解了为什么需要它,让我们来了解如何实现它。但是首先,让我们为表单创建一个服务器操作。
具有服务器操作的表单
在您的Next.js应用程序的app
目录下创建一个名为actions
的目录。现在在actions
文件夹下创建一个名为user.js
的文件,并使用以下代码:
"use server"
export async function updateUser(formData) {
const name = formData.get('name');
console.log(name);
// With the name, do anything, save in DB, create invoice, whatever!
}
这就是在Next.js中创建服务器函数的方法。文件顶部必须有一个”use server”
指令,以告诉Next.js这是一个特殊的文件,包含一个或多个异步函数,在服务器上执行。
Также у нас есть действие сервера (асинхронная функция) `updateUser` с `formData` в качестве аргумента. Внутри определения функции мы извлекаем значение `name` и выводим его в консоль.
Теперь посмотрим, как прикрепить это действие сервера к форме. Для этого создадим папку с именем `components` в корневом каталоге проекта. Затем создадим файл с именем `user-form.jsx` следующего содержимого:
import { Input } from "./ui/input"
import { Button } from "./ui/button"
import { updateUser } from "@/app/actions/user"
const UserForm = () => {
return(
<form className="p-4 flex" action={updateUser}>
<Input className="w-1/2 mx-2" type="text" name="name" />
<Button type="submit">Update User Name</Button>
</form>
)
}
export default UserForm;
Компонент React с простым формой. Форма содержит один текстовый полевой ввод с именем `name` и кнопку submit для отправки формы. В атрибуте `action` у формы имеется значение действия сервера `updateUser`. Теперь, когда форма отправляется с значением `name`, это значение станет частью данных формы, как мы обсуждали выше.
Протестируем это. Для этого создадим Next.js маршрут и страницу, где мы можем использовать компонент `UserForm`. Создадим папку с именем `extra-args` в каталоге `app`. Затем создадим файл `page.js` в каталоге `app/extra-args` следующего содержимого:
import UserForm from "@/components/user-form";
const ExtraArgsDemo = () => {
return (
<UserForm />
)
}
export default ExtraArgsDemo;
Этот простой компонент React, где мы импортируем компонент `UserForm` и используем его в JSX. Теперь запустите локальный сервер и перейдите к маршруту `localhost:3000/extra-args`. Вы должны увидеть форму с текстовым полем и кнопкой.
Введите какое-то текстовое значение в текстовое поле и нажмите кнопку.
Теперь вы сможете увидеть, что введенный текст был напечатан на консоли сервера. Почему на консоли сервера, а не в консоли браузера? Это потому, что действия сервера выполняются на сервере, а не на клиентской стороне браузера.
Таким образом, мы установили поток данных следующего состава:
Страница => Форма => Серверное действие
На странице есть форма. Когда форма отправляется, она выполняет серверное действие. Серверное действие печатает данные формы на консоль сервера.
Теперь посмотрим, как можно улучшить эти элементы, чтобы передавать дополнительные аргументы серверному действию.
Как передавать дополнительные аргументы
Передадим свойство userId
компоненту UserForm
с страницы. Мы передадим значение userId
, чтобы предположить, что мы программно передаем этот userId
нашу форму и серверное действие.
import UserForm from "@/components/user-form";
const ExtraArgsDemo = () => {
return (
<UserForm userId={"1234"} />
)
}
export default ExtraArgsDemo;
В компоненте UserForm
мы принимаем свойство userId
. Теперь нам нужно сделать что-то особенное, чтобы передать этот userId
серверному действию updateUser
.
JavaScript имеет магический метод bind()
, который помогает создать частично примененную функцию
. С помощью этой частично примененной функции вы можете создать функцию из другой функции с предварительно заданными аргументами.
В нашем случае функция updateUser
уже имеет аргумент с именем formData
. Теперь мы можем передать userId
в качестве дополнительного аргумента, используя метод bind()
для создания новой функции.
const updatedUserWithId = updateUser.bind(null, userId);
Первым аргументом метода bind()
является контекст, с которым вы привязываете функцию. Контекст обрабатывает связь функции с значением ключевого слова this
. В нашем случае мы можем оставить его null
, так как мы его не изменяем. Затем мы передаем новый аргумент userId
. Useful to know that the bind()
method works on both server and client components.
Вот модифицированный компонент UserForm
(файл user-form.jsx
). Обратите внимание, что значение действия формы теперь изменено на новую функцию updatedUserWithId
.
import { Input } from "./ui/input"
import { Button } from "./ui/button"
import { updateUser } from "@/app/actions/user"
const UserForm = ({userId}) => {
const updatedUserWithId = updateUser.bind(null, userId);
return(
<form className="p-4 flex" action={updatedUserWithId}>
<Input className="w-1/2 mx-2" type="text" name="name" />
<Button type="submit">Update User Name</Button>
</form>
)
}
export default UserForm;
Теперь серверное действие будет получать значение userId
в качестве аргумента. Также let’s print that to the console as well.
"use server"
export async function updateUser(userId, formData) {
const name = formData.get('name');
console.log(userId);
console.log(name);
// Do anything with the user id and name, save in DB,
// create invoice, whatever!
}
Если вы подадите форму с значением для name:
Вы увидите, что значения userId и name будут записаны на серверном консоле. Great! We have logged one value from the form data, and the other one was passed internally to the server action.
Таким образом, мы научились, как передавать дополнительные аргументы в серверное действие вместе с данными формы.
А что об скрытых полях?
HTML поддерживает скрытое поле формы для передачи данных клиента на сервер без принятия ввода от пользователей. Это означает, что мы могли бы использовать скрытое поле для передачи значения userId
следующим образом:
Зачем же мы делали все это с помощью метода bind()
? Все дело в безопасности. При передаче данных с использованием скрытых полей значение будет частью отображаемого HTML и не будет закодировано. Поэтому лучше обрабатывать это программно.
Ресурсы
На этом пока все. Вам понравилась эта статья? Вы что-то новое узнали? Если да, мне было бы интересно узнать, был ли контент полезным. Позвольте мне поделиться несколькими дополнительными ресурсами, которые вам могут понадобиться:
-
Весь исходный код, использованный в этой статье, можно найти на моем GitHub.
-
Вот Курс по обработке серверных действий с шаблонами и проектом.
-
Вот официальная документация действия сервера, если вы хотите больше читать.
-
А также вы можете больше узнать о методе bind() здесь.
Кроме того, вы можете связаться с мной:
-
Подписавшись на мой YouTube-канал. Если вы хотите учиться
React
и его экосистеме, такие какNext.js
, с основными концепциями и проектами, у меня есть для вас хорошие новости: вы можете посмотреть этот плейлист на моем YouTube-канале, который содержит более 25 видеоуроков и более 15 часов интересного содержания на данный момент, бесплатно. Я надеюсь, что вам понравится. -
Следуйте за мной на X (Twitter) или LinkedIn, если вы хотите не пропустить ежедневную дозу усовершенствования навыков.
-
Проверить и подписаться за моим Open Source-работы на GitHub.
-
Я регулярно публикую значимые заметки на моем GreenRoots Blog, их может быть полезно и для вас.
Скоро я вас жду с моим следующим статьей. До увидения вас следующим раз, пожалуйста, ухаживайте за собой, и продолжайте учиться.
Source:
https://www.freecodecamp.org/news/how-to-pass-additional-arguments-to-nextjs-server-actions/