Mutação assíncrona de dados e tratamento é uma tarefa necessária em aplicações web modernas. Você pode querer executar uma função assíncrona independente no servidor para realizar tarefas como salvar dados no armazenamento de dados, enviar e-mails, baixar PDFs, processar imagens e outros.

Next.js fornece-nos com Ações do Servidor que são funções assíncronas que executam no servidor. Podemos usar as ações do servidor para mutações de dados no servidor, mas as ações do servidor podem ser chamadas de componentes tanto do servidor quanto do cliente.

As ações do servidor são uma ótima maneira de tratar submissões de formulários executando a ação quando os dados do formulário forem submetidos. Neste artigo, vamos olhar para um caso de uso prático de tratamento de argumentos adicionais em ações de servidor do Next.js.

Se você estiver interessado em aprender Ações de Servidor do Next.js com padrões de projeto e construção de projetos, eu criei um curso rápido para você que pode ser encontrado aqui.

Também, este artigo está disponível como um tutorial de vídeo aqui:

Sumário

  1. Por que você precisaria passar argumentos adicionais?

  2. Um Formulário com uma Ação de Servidor

  3. Como Passar Argumentos Adicionais

  4. E quanto aos Campos Ocultos?

  5. Recursos

Por que você precisaria passar argumentos adicionais?

Quando executamos uma ação no servidor após a submissão de um formulário, a ação de servidor obtém automaticamente os dados do formulário. Por exemplo, veja o formulário abaixo:

<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>

Aqui, estamos executando uma ação de servidor chamada updateUser quando o formulário for submetido. A função updateUser receberá os dados do formulário submetido como um argumento, que pode ser usada para extrair os valores dos campos do formulário.

Como você pode ver no snippet de código abaixo, a função updateUser recebe um formData como argumento, e nós podemos extrair o valor do campo name a partir dele.

"use server"

export async function updateUser(formData) {
  const name = formData.get('name');
  console.log(name);
}

Enquanto este padrão cobre a maioria dos casos de uso básico, você pode precisar passar argumentos adicionais de forma programática para as ações do servidor. Esses argumentos não são parte do formulário ou dos dados de entrada do usuário. Elas são valores passados de forma programática para sua ação de servidor.

Para entender isso, veja o trecho de código da ação do servidor abaixo. É a mesma ação de servidor que vimos antes, mas nós passamos um argumento userId adicional juntamente com o argumento regular formData.

"use server"

export async function updateUser(userId, formData) {
  const name = formData.get('name');
  console.log(userId);
  console.log(name);
}

O valor de userId é algo interno à aplicação – e você não perguntaria ao usuário para enviar o valor como parte do envio do formulário. Em vez disso, você precisará passá-lo de forma programática para sua ação de servidor para executar cálculos adicionais.

Bem, esse é o caso de uso de que estamos falando. Enquanto entendemos por que precisamos disso, vamos entender como alcançar isso. Mas antes, vamos criar um formulário e uma ação de servidor funcional para ele.

Um Formulário Com Uma Ação de Servidor

Crie um diretório chamado actions dentro do diretório app do seu aplicativo Next.js. Agora, crie um arquivo user.js na pasta actions com o seguinte código:

"use server"

export async function updateUser(formData) {
  const name = formData.get('name');
  console.log(name);

  // Faz qualquer coisa com o nome, salva no banco de dados, cria fatura, o que quer que seja!
}

Este é o jeito de criar uma função de servidor em Next.js. Ele deve ter uma diretiva ”use server” no topo do arquivo para informar ao Next.js que este é um arquivo especial com uma ou mais funções assíncronas a serem executadas no servidor.

Temos então a ação do servidor (a função assíncrona) updateUser com formData como argumento. Dentro da definição da função, extraímos o valor de name e imprimimos-no no console.

Vamos agora anexar esta ação do servidor the um formulário. Para fazer isso, crie uma pasta chamada components na raiz da pasta do projeto. Crie um arquivo chamado user-form.jsx com o seguinte código:

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;

Este é um simples componente do React com um formulário. O formulário tem um campo de texto de entrada chamado name e um botão de envio para enviar o formulário. O formulário tem um atributo action com a ação do servidor updateUser como valor. Agora, quando o formulário for enviado com um valor de name, a ação do servidor vai recebê-lo como parte dos dados do formulário, como discutimos acima.

Vamos testá-lo. Para fazer isso, vamos criar uma rota e página do Next.js onde podemos usar o componente UserForm. Crie uma pasta chamada extra-args na pasta app. Agora, crie um arquivo chamado page.js na pasta app/extra-args com o seguinte código:

import UserForm from "@/components/user-form";

const ExtraArgsDemo = () => {
  return (
    <UserForm />
  )
}

export default ExtraArgsDemo;

Este é um simples componente do React onde importamos o componente UserForm e o usamos no JSX. Agora execute o servidor local e acesse esta rota localhost:3000/extra-args. Você deveria ver o formulário com um campo de texto e um botão.

Digite algum texto dentro do campo de texto e clique no botão.

Agora, você será capaz de ver que o texto digitado foi impresso no console do servidor. Por que no console do servidor? Por que não no console do navegador? Isso porque as ações do servidor executam no servidor, e não no lado do cliente do navegador.

Então, com isso, nós agora tivemos estabelecido um fluxo de dados assim:

Página => Formulário => Ação de Servidor

A página tem um formulário. O formulário executa uma ação de servidor na submissão. A ação de servidor imprime dados de formulário no console do servidor.

Vamos agora melhorar essas peças para passar argumentos adicionais para a ação de servidor.

Como Passar Argumentos Adicionais

Vamos passar uma propriedade ao componente UserForm da página. Vamos passar um userId com um valor para fingir que estamos passando este userId programaticamente para nosso formulário e para a ação de servidor a partir daí.

import UserForm from "@/components/user-form";

const ExtraArgsDemo = () => {
  return (
    <UserForm userId={"1234"} />
  )
}

export default ExtraArgsDemo;

No componente UserForm, nós aceitamos a propriedade userId. Agora, nós precisamos fazer algo especial para passar este userId para a ação de servidor updateUser.

O JavaScript tem um método mágico chamado bind() que nos ajuda a criar uma Função Aplicada Parcialmente. Com essa função aplicada parcialmente, você pode criar uma função a partir de outra função com argumentos pré-definidos.

No nosso caso, a função `updateUser` já tem um argumento chamado `formData`. Agora, podemos passar `userId` como um argumento adicional usando o método `bind()` para criar uma nova função.

const updatedUserWithId = updateUser.bind(null, userId);

O primeiro argumento do método `bind()` é o contexto ao qual você está associando a função. O contexto gerencia a associação da função com o valor da palavra-chave `this`. Em nosso caso, podemos manter como `null` pois não estamos mudando. Depois disso, passamos o novo argumento `userId`. É bom saber que o método `bind()` funciona tanto em componentes do servidor quanto em componentes do cliente.

Aqui está o componente `UserForm` modificado (arquivo `user-form.jsx`). Note que o valor de ação do formulário agora é modificado para a nova função `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;

Agora, a ação do servidor receberá o valor de `userId` como um argumento. Vamos imprimir isso no console também.

"use server"

export async function updateUser(userId, formData) {
  const name = formData.get('name');
  console.log(userId);
  console.log(name);

  // Faça alguma coisa com o id do usuário e o nome, salve no banco de dados,
  // crie fatura, o que quer que seja!
}

Se você submeter o formulário com um valor de nome:

Você verá que ambos os valores de `userId` e `name` são registrados no console do servidor. Ótimo! Nós registramos um valor do formulário e o outro foi passado internamente para a ação do servidor.

Então, aprendemos como passar argumentos extras para a ação do servidor juntamente com os dados do formulário.

E quanto aos campos ocultos?

O HTML suporta um campo de formulário do tipo oculto para passar dados do cliente para o servidor sem aceitar a entrada dos usuários. Portanto, isso significa que poderíamos ter usado o campo oculto para passar o valor userId assim:

Então, por que fizemos tudo isso com o método bind()? Bem, por questões de segurança. Quando você passa dados usando campos ocultos, o valor fará parte do HTML renderizado e também não será codificado. Então, é melhor lidar com isso programaticamente.

Recursos

Isso é tudo por agora. Você gostou de ler este artigo e aprendeu algo novo? Se sim, adoraria saber se o conteúdo foi útil. Deixe-me compartilhar alguns recursos adicionais que você pode precisar:

Além disso, você pode se conectar comigo por:

  • Assinando meu Canal do YouTube. Se você estiver disposto a aprender React e seu ecossistema, como Next.js, com conceitos básicos e projetos, tenho boas notícias para você: você pode ver essa playlist no meu canal do YouTube com mais de 25 tutoriais de vídeo e 15+ horas de conteúdo engajante até agora, de graça. Espero que você goste deles também.

  • Seguir comigo em X (Twitter) ou LinkedIn se você não quiser perder a dose diária de dicas de aprimoramento.

  • Ver e seguir meu trabalho de Código Aberto no GitHub.

  • Eu publico regularmente posts significativos no meu GreenRoots Blog, você pode achar que são úteis, também.

Vejo você logo com meu próximo artigo. Até então, por favor, cuide de si mesma e continue aprendendo.