O componente <form>
embutido do navegador permite criar controles interativos para enviar informações.
<form action={search}>
<input name="query" />
<button type="submit">Pesquisar</button>
</form>
- Referência
- Uso
- Lidar com o envio do formulário no cliente
- Lidar com o envio do formulário com uma Ação do Servidor
- Exibir um estado de pendência durante o envio do formulário
- Atualizando otimisticamente os dados do formulário
- Tratando erros de envio do formulário
- Exibir um erro de envio do formulário sem JavaScript
- Tratando múltiplos tipos de envio
Referência
<form>
Para criar controles interativos para enviar informações, renderize o componente <form>
embutido do navegador.
<form action={search}>
<input name="query" />
<button type="submit">Pesquisar</button>
</form>
Props
<form>
suporta todas as props comuns de elementos.
action
: uma URL ou função. Quando uma URL é passada para action
, o formulário se comportará como o componente de formulário HTML. Quando uma função é passada para action
, a função lidará com o envio do formulário. A função passada para action
pode ser assíncrona e será chamada com um único argumento contendo os dados do formulário do formulário enviado. A prop action
pode ser sobrescrita por um atributo formAction
em um componente <button>
, <input type="submit">
ou <input type="image">
.
Ressalvas
- Quando uma função é passada para
action
ouformAction
, o método HTTP será POST, independentemente do valor da propmethod
.
Uso
Lidar com o envio do formulário no cliente
Passe uma função para a prop action
do formulário para executar a função quando o formulário for enviado. formData
será passado para a função como um argumento, para que você possa acessar os dados enviados pelo formulário. Isso difere da ação HTML convencional, que aceita apenas URLs.
export default function Search() { function search(formData) { const query = formData.get("query"); alert(`Você pesquisou por '${query}'`); } return ( <form action={search}> <input name="query" /> <button type="submit">Pesquisar</button> </form> ); }
Lidar com o envio do formulário com uma Ação do Servidor
Renderize um <form>
com um campo de entrada e botão de envio. Passe uma Ação do Servidor (uma função marcada com 'use server'
) para a prop action
do formulário para executar a função quando o formulário for enviado.
Passar uma Ação do Servidor para <form action>
permite que os usuários enviem formulários sem JavaScript habilitado ou antes que o código tenha sido carregado. Isso é benéfico para usuários que têm uma conexão lenta, um dispositivo com pouca capacidade ou têm JavaScript desabilitado, e é semelhante à forma como os formulários funcionam quando uma URL é passada para a prop action
.
Você pode usar campos de formulário ocultos para fornecer dados à ação do <form>
. A Ação do Servidor será chamada com os dados dos campos de formulário ocultos como uma instância de FormData
.
import { updateCart } from './lib.js';
function AddToCart({productId}) {
async function addToCart(formData) {
'use server'
const productId = formData.get('productId')
await updateCart(productId)
}
return (
<form action={addToCart}>
<input type="hidden" name="productId" value={productId} />
<button type="submit">Adicionar ao Carrinho</button>
</form>
);
}
Em vez de usar campos de formulário ocultos para fornecer dados à ação do <form>
, você pode chamar o bind
para fornecê-lo com argumentos extras. Isso irá vincular um novo argumento (productId
) à função, além do formData
que é passado como um argumento para a função.
import { updateCart } from './lib.js';
function AddToCart({productId}) {
async function addToCart(productId, formData) {
"use server";
await updateCart(productId)
}
const addProductToCart = addToCart.bind(null, productId);
return (
<form action={addProductToCart}>
<button type="submit">Adicionar ao Carrinho</button>
</form>
);
}
Quando <form>
é renderizado por um Componente do Servidor, e uma Ação do Servidor é passada para a prop action
do <form>
, o formulário é progressivamente aprimorado.
Exibir um estado de pendência durante o envio do formulário
Para exibir um estado de pendência ao enviar um formulário, você pode chamar o Hook useFormStatus
em um componente renderizado em um <form>
e ler a propriedade pending
retornada.
Aqui, usamos a propriedade pending
para indicar que o formulário está sendo enviado.
import { useFormStatus } from "react-dom"; import { submitForm } from "./actions.js"; function Submit() { const { pending } = useFormStatus(); return ( <button type="submit" disabled={pending}> {pending ? "Enviando..." : "Enviar"} </button> ); } function Form({ action }) { return ( <form action={action}> <Submit /> </form> ); } export default function App() { return <Form action={submitForm} />; }
Para saber mais sobre o Hook useFormStatus
, consulte a documentação de referência.
Atualizando otimisticamente os dados do formulário
O Hook useOptimistic
fornece uma maneira de atualizar otimisticamente a interface do usuário antes que uma operação de segundo plano, como uma requisição de rede, seja concluída. No contexto dos formulários, essa técnica ajuda a tornar os aplicativos mais responsivos. Quando um usuário envia um formulário, em vez de esperar pela resposta do servidor para refletir as alterações, a interface é imediatamente atualizada com o resultado esperado.
Por exemplo, quando um usuário digita uma mensagem no formulário e pressiona o botão “Enviar”, o Hook useOptimistic
permite que a mensagem apareça imediatamente na lista com um rótulo “Enviando…”, mesmo antes da mensagem ser realmente enviada para um servidor. Essa abordagem “otimista” dá a impressão de velocidade e responsividade. O formulário então tenta realmente enviar a mensagem em segundo plano. Assim que o servidor confirma que a mensagem foi recebida, o rótulo “Enviando…” é removido.
import { useOptimistic, useState, useRef } from "react"; import { deliverMessage } from "./actions.js"; function Thread({ messages, sendMessage }) { const formRef = useRef(); async function formAction(formData) { addOptimisticMessage(formData.get("message")); formRef.current.reset(); await sendMessage(formData); } const [optimisticMessages, addOptimisticMessage] = useOptimistic( messages, (state, newMessage) => [ ...state, { text: newMessage, sending: true } ] ); return ( <> {optimisticMessages.map((message, index) => ( <div key={index}> {message.text} {!!message.sending && <small> (Enviando...)</small>} </div> ))} <form action={formAction} ref={formRef}> <input type="text" name="message" placeholder="Olá!" /> <button type="submit">Enviar</button> </form> </> ); } export default function App() { const [messages, setMessages] = useState([ { text: "Olá!", sending: false, key: 1 } ]); async function sendMessage(formData) { const sentMessage = await deliverMessage(formData.get("message")); setMessages([...messages, { text: sentMessage }]); } return <Thread messages={messages} sendMessage={sendMessage} />; }
Tratando erros de envio do formulário
Em alguns casos, a função chamada pela prop action
de um <form>
lança um erro. Você pode tratar esses erros envolvendo o <form>
em um Error Boundary. Se a função chamada pela prop action
de um <form>
lançar um erro, o fallback para a boundary de erro será exibido.
import { ErrorBoundary } from "react-error-boundary"; export default function Search() { function search() { throw new Error("erro de pesquisa"); } return ( <ErrorBoundary fallback={<p>Ocorreu um erro ao enviar o formulário</p>} > <form action={search}> <input name="query" /> <button type="submit">Pesquisar</button> </form> </ErrorBoundary> ); }
Exibir um erro de envio do formulário sem JavaScript
Exibir uma mensagem de erro de envio do formulário antes que o pacote JavaScript carregue para aprimoramento progressivo requer que:
<form>
seja renderizado por um Componente do Servidor- a função passada para a prop
action
do<form>
seja uma Ação do Servidor - o Hook
useActionState
seja usado para exibir a mensagem de erro
useActionState
aceita dois parâmetros: uma Ação do Servidor e um estado inicial. useActionState
retorna dois valores, uma variável de estado e uma ação. A ação retornada pelo useActionState
deve ser passada para a prop action
do formulário. A variável de estado retornada pelo useActionState
pode ser usada para exibir uma mensagem de erro. O valor retornado pela Ação do Servidor passada para useActionState
será usado para atualizar a variável de estado.
import { useActionState } from "react"; import { signUpNewUser } from "./api"; export default function Page() { async function signup(prevState, formData) { "use server"; const email = formData.get("email"); try { await signUpNewUser(email); alert(`Adicionado "${email}"`); } catch (err) { return err.toString(); } } const [message, signupAction] = useActionState(signup, null); return ( <> <h1>Inscreva-se para minha newsletter</h1> <p>Inscreva-se com o mesmo email duas vezes para ver um erro</p> <form action={signupAction} id="signup-form"> <label htmlFor="email">Email: </label> <input name="email" id="email" placeholder="react@example.com" /> <button>Inscrever-se</button> {!!message && <p>{message}</p>} </form> </> ); }
Saiba mais sobre atualizar o estado a partir de uma ação de formulário com a documentação do useActionState
Tratando múltiplos tipos de envio
Os formulários podem ser projetados para lidar com várias ações de envio, com base no botão pressionado pelo usuário. Cada botão dentro de um formulário pode ser associado a uma ação ou comportamento distinto definindo a prop formAction
.
Quando um usuário clica em um botão específico, o formulário é enviado, e uma ação correspondente, definida pelos atributos e ação daquele botão, é executada. Por exemplo, um formulário pode enviar um artigo para revisão por padrão, mas ter um botão separado com formAction
definido para salvar o artigo como um rascunho.
export default function Search() { function publish(formData) { const content = formData.get("content"); const button = formData.get("button"); alert(`'${content}' foi publicado com o botão '${button}'`); } function save(formData) { const content = formData.get("content"); alert(`Seu rascunho de '${content}' foi salvo!`); } return ( <form action={publish}> <textarea name="content" rows={4} cols={40} /> <br /> <button type="submit" name="button" value="submit">Publicar</button> <button formAction={save}>Salvar rascunho</button> </form> ); }