React Curso

Configuração Ambiente

React é o framework que entende JSX e transforma tudo em interface (UI). JSX sozinho não funciona sem React. React é quem entende JSX, transforma em HTML real no navegador, e cuida da interface atualizando quando algo muda.

Primeiro passo é instalar o node.JS

As extensões do VsCode para os icones do projeto é a vscode-icons. e para estilização dos componentes instale vscode-styled-components.

Boa organização e estrutura de pastas:

frontend/
src/

├── app/                  # Páginas e Rotas (Next.js App Router)
│   ├── page.tsx          # Página inicial/principal
│   └── ...               # Outras rotas

├── components/           # Componentes reutilizáveis (botões, inputs, cards, etc.)
│   └── MeuBotao.tsx

├── features/             # Funcionalidades por domínio (Ex: auth, posts, perfil)
│   └── auth/
│       ├── LoginForm.tsx
│       ├── AuthService.ts
│       └── authSlice.ts (se usarmos Redux depois)

├── hooks/                # Hooks personalizados
│   └── useAuth.ts

├── services/             # Comunicação com APIs
│   └── api.ts            # fetch ou axios, com funções para backend

├── themes/               # Temas do MUI
│   └── theme.ts

├── types/                # Tipagens TypeScript compartilhadas
│   └── user.ts

├── utils/                # Funções utilitárias
│   └── formatDate.ts

└── shared/               # Componentes e estilos que são genéricos (ex: layout)
    └── Layout.tsx

📂 Explicação das pastas
Pasta Para quê serve?
app/ Arquivos de rota (cada pasta é uma URL), fornecido pelo App Router do Next.js
components/ Componentes pequenos e reutilizáveis (Botões, Inputs, Cards, etc.)
features/ Agrupa arquivos por funcionalidade ou domínio do sistema (ex: login, cadastro, usuários...)
hooks/ Hooks personalizados com lógica reaproveitável (useForm, useAuth, etc.)
services/ Código que faz requisições HTTP, autenticação, integração com a API (DRF futuramente)
themes/ Temas e customizações do MUI
types/ Interfaces e tipos TypeScript para organização de dados
utils/ Funções genéricas auxiliares que não são específicas de um componente
shared/ Layouts, wrappers ou contextos globais, estilos que todos os arquivos podem usar

Recomenda-se criar uma pasta frontend e aplicar os comando dentro dela, para organizar o projeto.
 

Comandos React

Durante o processo de instalação, esse é o padrão que aderi. Com adendo para usar tailwind e turbopack, que pode ser escolhido como sim.

✔ Would you like to use ESLint with this project? … Yes
✔ Would you like to use Tailwind CSS with this project? … No
✔ Would you like to use `src/` directory with this project? … Yes
✔ Would you like to use App Router? … Yes
✔ Would you like to customize the default import alias (@/*)? … Yes
✔ Would you like to use Turbopack as the bundler? … No
✔ What import alias would you like configured? » @/* Press ENTER to Default

bash
$ npx create-next-app . --ts # Esse comando cria um projeto React usando o Next.js com TypeScript.
$ npm install @mui/material @emotion/react @emotion/styled # Instala o MUI e o emotion
tsconfig.json
//...
    "baseUrl": "./src",
    "paths": {
      "@/*": [
        "./*"
      ]
    }
//...

Adicione baseUrl antes de path e deixe o path como no exemplo. isso informa que será usado @ para definir os caminhos.

sintaxes_jsx.tsx
const nome = "João";
{nome && <p>Olá, {nome}</p>} // Isso é um if, se nome não for vazio exiba: Olá, João.

Material UI (MUI)

Material UI (MUI) é uma biblioteca de componentes React baseada no Material Design — um sistema visual criado pelo Google. Não precisa criar botões, inputs, cards etc. do zero, tem o design profissional: tudo já vem com aparência moderna e responsiva e é altamente personalizável: se quiser mudar cores, estilos, etc., é possível.

o MUI não é só "estilo", ele já vem com componentes React prontos. O MUI usa Emotion por padrão para aplicar estilos nos componentes (por debaixo dos panos). Emotion é uma biblioteca de CSS-in-JS, ou seja, permite que você escreva estilos CSS diretamente dentro do JavaScript/TypeScript.

| Criando e Aplicando Tema MUI

theme.ts
//src/themes/theme.ts
import { createTheme } from '@mui/material/styles';

// Criamos um tema com as cores e configurações que desejamos
const theme = createTheme({
  palette: {
    primary: {
      main: '#1976d2', // Azul principal
    },
    secondary: {
      main: '#dc004e', // Rosa secundário
    },
  },
});

export default theme;

O tema é a configuração global do MUI que define cores, fontes, etc. 

palette define o esquema de cores. Essas cores podem ser usadas em todo o app, por exemplo: <Button color="primary" />.

createTheme: Esta função cria o objeto de tema do MUI. O MUI usa este objeto para personalizar os componentes.

ThemeRegistry.tsx
// src/themes/ThemeRegistry.tsx
"use client";

import * as React from "react"; // Importa o React (necessário para JSX funcionar)
import { ThemeProvider } from "@mui/material/styles"; // Componente do MUI que aplica o tema
import CssBaseline from "@mui/material/CssBaseline"; // Reset de estilos padrão
import theme from "./theme"; // Importa o tema que foi definido (cores, fontes, etc.)

export default function ThemeRegistry({
  children, // Aqui recebemos os "filhos" que vão ser envolvidos por esse wrapper
}: {
  children: React.ReactNode; // Tipagem: children é um ou mais elementos React (JSX)
}) {
  return (
    <ThemeProvider theme={theme}>
      <CssBaseline />
      {children}
    </ThemeProvider>
  );
}

Esse arquivo está dizendo: tudo que estiver dentro de <body> vai usar esse tema do MUI e ter o CSS básico reiniciado.
ThemeProvider é o fornecedor de contexto do Material UI. Ele injeta o tema personalizado na aplicação toda. Isso permite, por exemplo, mudar a cor principal, fonte padrão, bordas etc. MUI ThemeProvider precisa estar no lado do cliente, por isso criamos um wrapper separado com use client. Ness caso ThemeRegistry.tsx é um wrapper, componente que envolve toda sua aplicação para aplicar o tema do Material UI e o reset de CSS.

{children}:
Aqui ele renderiza o que foi envolvido por esse componente. No caso, todo o conteúdo da sua aplicação que está dentro de <ThemeRegistry> lá no layout.tsx.

layout.tsx
// src/app/layout.tsx
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import ThemeRegistry from "@/themes/ThemeRegistry";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "Meu Projeto",
  description: "React + Next.js + MUI + DRF",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="pt-br">
      <body className={`${geistSans.variable} ${geistMono.variable}`}>
        {/* Aqui é onde o MUI vai ser aplicado, mas fora do layout principal */}
        <ThemeRegistry>{children}</ThemeRegistry>
      </body>
    </html>
  );
}

layout.tsx é um componente server-side por padrão.

page.tsx
// src/app/page.tsx
"use client"; // Esse componente vai ser executado no lado do cliente

import { useState } from "react";
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
import Container from "@mui/material/Container";

export default function HomePage() {
  const [mensagem, setMensagem] = useState(
    "Clique no botão para ver o tema em ação!"
  );

  return (
    <Container maxWidth="sm" sx={{ marginTop: 8, textAlign: "center" }}>
      <Typography variant="h4" gutterBottom>
        Testando o Tema do MUI
      </Typography>

      <Typography variant="body1" sx={{ mb: 2 }}>
        {mensagem}
      </Typography>

      <Button
        variant="contained"
        color="primary"
        onClick={() => setMensagem("Você clicou no botão!")}
      >
        Clique aqui
      </Button>
    </Container>
  );
}

Teste do thema.


Componentes

Um componente em React é uma função que retorna código JSX, que representa uma parte da interface (UI) da sua aplicação. Por padrão tanto o arquivo quanto o nome da função exportada devem estar com a primeira letra maiuscula.

JSX é JavaScript com aparencia de HTML.

| Componente Simples

MeuComponente.tsx
// src/components/MeuComponente.tsx
function MeuComponente() {
  return <h1>Olá, eu sou um componente!</h1>;
}
export default MeuComponente;

Esse código define um componente chamado MeuComponente, que mostra um título.

page.tsx
// src/app/page.tsx
import MeuComponente from "@/components/MeuComponente";
export default function HomePage() {
  return <MeuComponente />;
}

Usando o componente criado.


Hooks

Um hook é uma função especial que o React executa durante o ciclo de vida do componente.

Hook Serve para...
useState Guardar informações (como variáveis reativas)
useEffect Fazer ações quando algo muda
useRef Referenciar elementos diretamente
useContext Compartilhar informações em toda a aplicação
UseState

No React, o state é usado para guardar informações que podem mudar com o tempo. Toda vez que o estado é mudado, ele atualiza a tela automaticamente. Por exemplo: Um contador que aumenta quando você clica num botão, mostrar ou esconder um texto na tela, trocar o conteúdo de algo dependendo de uma ação

Pra isso, usamos um hook chamado useState.

useState cria um estado local no componente — ou seja, uma variável que pode mudar com o tempo e re-renderiza o componente quando muda.

Sintaxe: const [estado, setEstado] = useState(valorInicial);

  • estado: é o valor atual (por exemplo, 0, true, "olá", etc.)
  • setEstado: é a função que altera esse valor
  • useState(valorInicial): define o valor inicial do estado

No React, quando você usa setContador ou qualquer setAlgo, o componente renderiza de novo automaticamente. A variável estado não deve ser mudada diretamente. pois não gera atualização de interface, apenas pela funcão set.

UseEffect

useEffect é usado para executar efeitos colaterais (Eventos externos que acontencem fora da renderização do JSX, Ex: buscar dados da API) em um componente.

hook_useState.tsx
'use client';
import { useState } from 'react';
import { Button } from '@mui/material';

export default function Contador() {
  const [contador, setContador] = useState(0); // começa em 0

  return (
    <>
      <h2>Contador: {contador}</h2>
      <Button variant="contained" onClick={() => setContador(contador + 1)}>
        Somar
      </Button>
    </>
  );
}

Quando o componente carrega pela primeira vez, contador é 0. Quando clicamos no botão, chamamos setContador(contador + 1), Isso muda o valor de contador, consequentemente o componente re-renderiza automaticamente e mostra o novo valor.

| Hook useState

Contador.tsx
// src/components/Contador.tsx
'use client';

import { useState } from "react";
import { Button, Typography, Stack } from "@mui/material";

export default function Contador() {
  const [contador, setContador] = useState(0);

  return (
    <Stack spacing={2} alignItems="center">
      <Typography variant="h6">Valor atual: {contador}</Typography>
      <Button variant="contained" onClick={() => setContador(contador + 1)}>
        Somar +1
      </Button>
      <Button variant="outlined" onClick={() => setContador(contador - 1)}>
        Subtrair -1
      </Button>
      <Button color="error" onClick={() => setContador(0)}>
        Resetar
      </Button>
    </Stack>
  );
}

Cria 3 botões que alteram o estado da variavel contador.

page.tsx
// src/app/page.tsx
import Contador from "@/components/Contador";

export default function HomePage() {
  return (
    <main>
      <Contador />
    </main>
  );
}
hook_useEffect.tsx
import { useEffect } from "react";

useEffect(() => {
  // código que será executado após o componente renderizar

  return () => {
    // cleanup opcional (executado quando o componente for destruído)
  };
}, []);

Sintaxe Basica.

O segundo parâmetro ([]) é chamado de array de dependências:

  • []: executa só uma vez (quando o componente monta)

  • [variavel]: executa sempre que a variável mudar

  • Sem array: executa em toda renderização

| Hook useEffect

BoasVindas.tsx
//src/components/BoasVindas.tsx
"use client";

import { useEffect } from "react";

// Componente que altera o título da aba do navegador
export default function BoasVindas() {
  // useEffect é chamado assim que o componente for exibido
  useEffect(() => {
    // Esse código só roda 1 vez (Mas não funciona com next.js - so para exemplo)
    document.title = "Seja bem-vindo!";

    // Só para você ver que o efeito foi ativado
    console.log("useEffect executado - título alterado");

    // Cleanup opcional (caso o componente saia da tela)
    return () => {
      console.log("Componente desmontado");
    };
  }, []); // Array vazio → só roda 1 vez

  return <p>Bem-vindo ao React com Next.js e MUI!</p>;
}

useEffect(() => { ... }, dependências): A função do useEffect é executar um código em determinados momentos da vida do componente. Esse "momento" depende das dependências que você passar no segundo parâmetro, que é esse array: [].

page.tsx
import BoasVindas from "@/components/BoasVindas";

export default function Home() {
  return (
    <div>
      <h1>Minha Página Inicial</h1>
      <BoasVindas />
    </div>
  );
}

Eventos e Manipulação de estado

Eventos são ações do usuário, como clicar em botões e etc, passar o mouse, e etc. Esse eventos são reagidos por funções conhecidas como handlers. No React, você escuta eventos com onChange ou onSubmit e armazena os valores com useState.

FormMensagem.tsx
// src/components/FormMensagem.tsx
'use client';

import { useState } from "react";
import { TextField, Button, Typography, Stack } from "@mui/material";

export default function FormMensagem() {
  const [mensagem, setMensagem] = useState("");
  const [mensagemFinal, setMensagemFinal] = useState("");

  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault(); // impede recarregar a página
    setMensagemFinal(mensagem);
    setMensagem(""); // limpa o campo
  };

  return (
    <Stack spacing={2} alignItems="center" component="form" onSubmit={handleSubmit}>
      <TextField
        label="Digite uma mensagem"
        value={mensagem}
        onChange={(e) => setMensagem(e.target.value)}
      />
      <Button variant="contained" type="submit">
        Enviar
      </Button>
      // Se hover mensagem exiba mensagem enviada. Um if em jsx.
      {mensagemFinal && (
        <Typography variant="h6" color="primary">
          Mensagem enviada: {mensagemFinal}
        </Typography>
      )}
    </Stack>
  );
}

Sempre trate formulários com:

  • useState para armazenar o valor

  • onChange para capturar mudanças

  • onSubmit para agir no envio

Obs.: </Typography> é um componente para textos estilizados (como título, parágrafo, subtítulo). Em vez de usar <h1>, <p>, etc, você usa Typography 


Props

Props (Abreviação de propriedades) são como parâmetros de função que você envia para um componente. São a forma de passar dados de um componente pai para um filho. Esses dados podem ser de qualquer tipo: strings, números, objetos, funções etc.

No React , quando um componente "envolve" ou "chama" outro componente e passa valores para ele, dizemos que:

  • O componente que chama é o pai (parent)

  • O componente que é chamado é o filho (child)

  • A comunicação por props é sempre unidirecional: do pai para o filho.

  • O filho não pode alterar diretamente o valor da prop (por isso elas são somente leitura).

 

| Props React

Botao.tsx
// src/components/Botao.tsx

type Props = {
  texto: string;
};

export default function Botao({ texto }: Props) { //Essa forma é desestruturação do objeto — igual ao Python com def funcao(**kwargs)
  return <button>{texto}</button>;
}

Foi criado um componente Botao, que recebe props — no caso, um texto, e retorna um <button> com esse texto dentro.

type: palavra-chave do TypeScript para definir um tipo personalizado

Props: é o nome que escolhemos para esse tipo (você pode chamar de BotaoProps, TituloProps, etc)

page.tsx
// src/app/page.tsx (página inicial)

import Botao from "@/components/Botao";

export default function Home() {
  return (
    <div>
      <h1>Minha Página</h1>
      <Botao texto="Clique aqui" />
      <Botao texto="Enviar" />
    </div>
  );
}

Este é o componente pai (Home) que chama o filho (Botao) duas vezes.

O componente Botao vai aparecer duas vezes com textos diferentes: “Clique aqui” e “Enviar”.

Caso tentasse usar o botão sem a prop (<Botao />) daria o erro: "Property 'texto' is missing in type '{}' but required in type 'Props'."


Context API (Contexto Global)

No React, o Context (ou Context API) permite compartilhar dados entre componentes sem precisar passar props manualmente em cada nível da árvore de componentes. Útil para temas, usuário logado, idioma, carrinho de compras, etc.

[Contexto] ←  guarda uma informação (ex: tema escuro ou claro, login, etc ...)

[Provider] ←  entrega essa informação para todos os componentes que estiverem "dentro" dele

[Hook] ←  acessa a informação onde for necessário

| Contexto de Tema

ThemeContext.tsx
// src/contexts/ThemeContext.tsx

"use client"; // ← necessário para rodar hooks no Next.js

import { createContext, useContext, useState } from "react";

// 1️⃣ Tipagem do que o contexto vai armazenar
type ThemeContextType = {
  modoEscuro: boolean;            // Estado: se está escuro ou não
  alternarTema: () => void;       // Função: alternar o tema
};

// 2️⃣ Criação do contexto (ainda sem valor) - Está em Ts, em Js seria: const ThemeContext = createContext(undefined);
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);

// 3️⃣ Componente que fornece o valor para os filhos
export function ThemeProvider({ children }: { children: React.ReactNode }) {
  const [modoEscuro, setModoEscuro] = useState(false); // cria o estado

  // Função para trocar de tema
  const alternarTema = () => setModoEscuro((anterior) => !anterior);

  // Aqui entregamos os dados do contexto para os filhos
  return (
    <ThemeContext.Provider value={{ modoEscuro, alternarTema }}>
      {children}
    </ThemeContext.Provider>
  );
}

// 4️⃣ Hook personalizado para acessar o contexto
export function useThemeContext() {
  const contexto = useContext(ThemeContext);

  if (!contexto) {
    throw new Error("useThemeContext deve estar dentro do ThemeProvider");
  }

  return contexto;
}

createContext: É uma função do React que cria um "contexto" — ou seja, um lugar centralizado para guardar dados e compartilhar entre componentes sem precisar passar props manualmente. const MeuContexto = createContext(valorInicial); Ela retorna .Provider → o componente que fornece os dados.

.Provider é o componente que: fornece um valor (state, função, objeto, etc) para todos os componentes filhos que estiverem dentro dele

layout.tsx
// src/app/layout.tsx

import { ThemeProvider } from "@/contexts/ThemeContext"; // nosso contexto

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="pt-br">
      <body>
        <ThemeProvider>
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

Com isso, toda a aplicação agora tem acesso ao nosso contexto.

BotaoTema.tsx
// src/components/BotaoTema.tsx

"use client"; // usamos hook aqui!

import { useThemeContext } from "@/contexts/ThemeContext";

export default function BotaoTema() {
  const { modoEscuro, alternarTema } = useThemeContext(); // pegamos os dados do contexto

  return (
    <button onClick={alternarTema}>
      {modoEscuro ? "Modo Claro" : "Modo Escuro"}
    </button>
  );
}

Botão para alterar tema.

page.tsx
// src/app/page.tsx

import BotaoTema from "@/components/BotaoTema";

export default function Home() {
  return (
    <div>
      <h1>Minha Página Inicial</h1>
      <BotaoTema />
    </div>
  );
}

Navegação com Next.js

a navegação entre páginas funciona baseado na estrutura de pastas dentro do diretório app/. Cada pasta representa uma rota (página) da aplicação.

app/
├── layout.tsx
├── page.tsx                ← Home (rota '/')
├── sobre/
│   └── page.tsx            ← Rota '/sobre'
└── contato/
    └── page.tsx            ← Rota '/contato'


O Next.js fornece um componente Link para navegar sem recarregar a página (SPA — single page application).

Menu.tsx
//src/components/Menu.tsx
"use client";

import Link from "next/link";

export default function Menu() {
  return (
    <nav style={{ display: "flex", gap: "10px" }}>
      <Link href="/">Início</Link>
      <Link href="/sobre">Sobre</Link>
      <Link href="/contato">Contato</Link>
    </nav>
  );
}


Supondo que tenha dois arquivos page.tsx: src/app/contato/page.tsxsrc/app/sobre/page.tsx.

layout.tsx
import "./globals.css";
import { ThemeProvider } from "@/contexts/ThemeContext";
import Menu from "@/components/Menu";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="pt-br">
      <body>
        <ThemeProvider>
          <Menu />
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

Inserir o menu dentro de layout para ficar disponivel em todas as paginas.


Formulário React MUI

Exemplo de criação de formulário completo.

Formulario.tsx
// src/components/Formulario.tsx
"use client";

import { useState } from "react";
import { TextField, Button, Box } from "@mui/material";

/**
 * Componente que exibe um formulário com campos de nome e email
 */
export default function Formulario() {
  const [nome, setNome] = useState(""); // Estado para armazenar o nome
  const [email, setEmail] = useState(""); // Estado para armazenar o email

  /**
   * Função chamada ao enviar o formulário
   */
  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault(); // Evita que a página recarregue
    console.log("Nome:", nome);
    console.log("Email:", email);
    alert(`Dados enviados:\nNome: ${nome}\nEmail: ${email}`);
  };

  return (
    <Box
      component="form"
      onSubmit={handleSubmit}
      sx={{
        display: "flex",
        flexDirection: "column",
        gap: 2,
        maxWidth: 400,
        margin: "auto",
        marginTop: 4,
      }}
    >
      {/* TextField é o campo de texto do MUI */}
      <TextField
        label="Nome"
        variant="outlined"
        value={nome}
        onChange={(e) => setNome(e.target.value)}
      />

      {/* Campo para email */}
      <TextField
        label="Email"
        type="email"
        variant="outlined"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
      />

      {/* Botão do MUI para enviar o formulário */}
      <Button type="submit" variant="contained" color="primary">
        Enviar
      </Button>
    </Box>
  );
}

Axios

A biblioteca Axios (instale com npm) é uma ferramenta muito usada em aplicações React (e outras também) para fazer requisições HTTP — como GET, POST, PUT, DELETE — para APIs, de forma mais simples e poderosa do que o fetch nativo do JavaScript. Axios é uma biblioteca JavaScript baseada em promises para fazer requisições HTTP.

Axios:

  • ✅ Tem sintaxe mais limpa

  • ✅ Já faz o parse automático do JSON

  • ✅ Lida melhor com erros

  • ✅ Permite interceptar requisições e respostas

  • ✅ Funciona do mesmo jeito no browser e no Node.js

| Consulta de API Externa

ApiService.ts
import axios from "axios";

export const ApiService = axios.create({
  baseURL: "http://localhost:9000",
  headers: {
    "Content-Type": "application/json",
  },
});
useIndex.ts
import { useEffect, useState } from "react";
import { Profissional } from "../../components/entidades/profissional";
import { ApiService } from "../../services/ApiService";

export default function useIndex() {
  const [listaProfissionais, setListaProfissionais] = useState<Profissional[]>(
    []
  );

  // concatena a url base com o endpoint vinda de ApiService.ts
  useEffect(() => {
    ApiService.get("/profissionais").then((response) => {
      setListaProfissionais(response.data);
    });
  }, []);
  return {
    listaProfissionais,
  };
}

SWR

SWR (Stale-While-Revalidat) é uma biblioteca para React  (instalada com npm) desenvolvida pela equipe da Vercel, usada para busca de dados remotos de forma eficiente, com foco em desempenho, simplicidade e reatividade. Ela é um padrão de cache que significa: "Use dados antigos (stale) enquanto revalida os dados em segundo plano.

✅ Principais vantagens:

  • Cache embutido: os dados são armazenados e reutilizados automaticamente.

  • Revalidação automática: atualiza os dados em segundo plano.

  • Requisições automáticas ao focar a aba/janela.

  • Evita chamadas duplicadas: ótima para performance.

  • Suporte a TypeScript, SSR, Suspense etc.

Substiui o Hook effect e use state, se encarregando de fazer a chamada. perguntando se esta no cache e se estiver, verifica se o cache é valido.

swr.jsx
import useSWR from 'swr'

const fetcher = url => fetch(url).then(res => res.json())

function Profile() {
  const { data, error, isLoading } = useSWR('/api/user', fetcher)

  if (error) return <div>Erro ao carregar</div>
  if (isLoading) return <div>Carregando...</div>

  return <div>Olá, {data.name}!</div>
}