O que react-router-framework-mode faz?
Construa aplicações React full-stack usando o modo framework do React Router. Utilize ao configurar rotas, trabalhar com loaders e actions, lidar com formulários, navegação, UI pendente/otimista, limites de erro ou ao trabalhar com react-router.config.ts ou outras convenções do React Router.
Visão Geral
O modo framework do React Router estende o roteador para oferecer recursos de servidor como carregamento de dados (loaders), manipulação de formulários (actions), e formulários HTML progressivos. Ele permite que você construa aplicações full-stack com React de forma declarativa.
Configuração
O arquivo react-router.config.ts na raiz do projeto define as configurações do modo framework:
import { type Config } from "@react-router/dev/config";
export default {
appDirectory: "app",
future: {
unstable_optimizeDeps: true,
},
} satisfies Config;
Rotas
As rotas são definidas usando o sistema de arquivos no diretório app/routes/ ou manualmente via plugin.
Exemplo de Rota
// app/routes/contacts.$contactId.tsx
import { type LoaderFunctionArgs } from "react-router";
import { getContact } from "../data";
export async function loader({ params }: LoaderFunctionArgs) {
const contact = await getContact(params.contactId);
return { contact };
}
export default function Contact() {
// Componente da rota
}
Loaders e Actions
- Loaders: Funções que fornecem dados para a rota. Elas rodam no servidor e são chamadas antes da renderização.
- Actions: Funções que manipulam dados (POST, PUT, PATCH, DELETE). Elas processam formulários e mutations.
Formulários
Use o componente <Form> do React Router para enviar dados com suporte a JavaScript desabilitado:
import { Form } from "react-router";
export default function NewContact() {
return (
<Form method="post">
<input name="name" />
<button type="submit">Salvar</button>
</Form>
);
}
Navegação e UI
- Navegação: Use
<Link>para navegação client-side. - UI Pendente: Mostre um indicador enquanto a navegação está carregando usando o hook
useNavigation.
import { useNavigation } from "react-router";
function SubmitButton() {
const navigation = useNavigation();
return (
<button type="submit" disabled={navigation.state === "submitting"}>
{navigation.state === "submitting" ? "Enviando..." : "Enviar"}
</button>
);
}
- UI Otimista: Atualize a interface imediatamente antes da confirmação do servidor usando
useFetcher.
Limites de Erro
Defina componentes de erro para capturar exceções:
export function ErrorBoundary() {
return (
<div>
<h1>Erro</h1>
<p>Algo deu errado.</p>
</div>
);
}
Convenções
- Diretório
app/: Contém todo o código da aplicação. - Arquivo
root.tsx: Layout raiz da aplicação. - Pasta
routes/: Rotas da aplicação. - Pasta
data/: Lógica de acesso a dados. - Pasta
styles/: Folhas de estilo.
Exemplo Completo
// app/routes/contacts.$contactId.tsx
import { Form, useLoaderData, useFetcher } from "react-router";
import { getContact, updateContact } from "../data";
import type { LoaderFunctionArgs, ActionFunctionArgs } from "react-router";
export async function loader({ params }: LoaderFunctionArgs) {
const contact = await getContact(params.contactId);
if (!contact) {
throw new Response("Not Found", { status: 404 });
}
return { contact };
}
export async function action({ request, params }: ActionFunctionArgs) {
const formData = await request.formData();
return updateContact(params.contactId, {
favorite: formData.get("favorite") === "true",
});
}
export default function Contact() {
const { contact } = useLoaderData<typeof loader>();
const fetcher = useFetcher();
return (
<div id="contact">
<h1>{contact.name}</h1>
<fetcher.Form method="post">
<button
name="favorite"
value={contact.favorite ? "false" : "true"}
>
{contact.favorite ? "★" : "☆"}
</button>
</fetcher.Form>
</div>
);
}
Recursos Adicionais