Fundamentos do Next.js 15
Interatividade e Gerenciamento de Estado com Client Components
Aprenda sobre interatividade e gerenciamento de estado com client components
🚀 Interatividade e Gerenciamento de Estado com Client Components
Olá! Bem-vindos à nossa aula prática sobre como trazer vida e dinamismo às suas aplicações Next.js 15 usando Client Components. Até agora, exploramos o poder dos Server Components para otimização e busca de dados, mas para criar interfaces ricas e responsivas que reagem às ações do usuário, precisamos dos Client Components.
Nesta aula, vamos mergulhar na criação de componentes interativos, gerenciando o estado local e respondendo a eventos do usuário. Prepare-se para codificar! 💻✨
1. Introdução: Onde a Magia Acontece ✨
Os Client Components são a ponte entre a renderização otimizada no servidor e a experiência interativa no navegador. Eles permitem que você use recursos específicos do navegador, como hooks de estado (useState, useReducer), hooks de efeito (useEffect), e manipuladores de eventos (onClick, onChange), que são essenciais para qualquer aplicação web moderna.
Quando usar Client Components?
- Quando você precisa de interatividade: botões, formulários, toggles, etc.
- Quando você precisa de estado: gerenciar dados que mudam com a interação do usuário.
- Quando você precisa de efeitos colaterais no navegador: acesso ao DOM, uso de APIs de navegador (geolocation, localStorage).
- Quando você precisa de componentes de terceiros que dependem de estado ou efeitos de navegador.
Lembre-se: o padrão no Next.js App Router é Server Components. Para transformar um componente em um Client Component, você precisa adicionar a diretiva 'use client' no topo do arquivo.
2. Explicação Detalhada: Construindo Interatividade 🛠️
Vamos ver como 'use client' habilita a interatividade e o gerenciamento de estado.
2.1. Marcando um Componente como Cliente ('use client')
A diretiva 'use client' é a primeira linha de código em um arquivo e sinaliza ao Next.js que este componente (e todos os seus filhos, a menos que explicitamente marcados como Server Components) deve ser renderizado e hidratado no cliente.
// app/components/MyClientComponent.jsx
'use client'; // <-- Esta linha é crucial!
import { useState } from 'react';
export default function MyClientComponent() {
const [count, setCount] = useState(0);
return (
<div>
<h1>Contador Interativo</h1>
<p>Você clicou {count} vezes.</p>
<button onClick={() => setCount(count + 1)}>
Clique-me!
</button>
</div>
);
}2.2. Gerenciando Estado com useState
O hook useState é a ferramenta fundamental do React para adicionar estado a componentes funcionais. Ele retorna um par de valores: o estado atual e uma função para atualizá-lo.
'use client';
import { useState } from 'react'; // 👈 Importe useState
export function Counter() {
const [count, setCount] = useState(0); // 👈 Inicializa o estado 'count' com 0
return (
<div>
<p>Contador: {count}</p>
<button onClick={() => setCount(count + 1)}>Incrementar</button> {/* 👈 Atualiza o estado */}
<button onClick={() => setCount(count - 1)}>Decrementar</button>
</div>
);
}2.3. Manipulando Eventos do Usuário
Client Components permitem que você responda a eventos como cliques (onClick), mudanças em inputs (onChange), submissões de formulários (onSubmit), e muito mais.
Exemplo: Input Controlado
'use client';
import { useState } from 'react';
export function ControlledInput() {
const [inputValue, setInputValue] = useState(''); // Estado para o valor do input
const handleChange = (event) => {
setInputValue(event.target.value); // Atualiza o estado com o valor do input
};
return (
<div>
<label htmlFor="myInput">Digite algo:</label>
<input
id="myInput"
type="text"
value={inputValue} // O valor do input é controlado pelo estado
onChange={handleChange} // O evento onChange atualiza o estado
className="border p-2 rounded-md"
/>
<p>Você digitou: {inputValue}</p>
</div>
);
}2.4. Combinando Interatividade e Estado
Podemos facilmente combinar múltiplos estados e manipuladores de eventos para criar componentes mais complexos.
Exemplo Oficial (Adaptado da documentação do React/Next.js para um contexto prático):
Vamos criar um componente que alterna a visibilidade de um texto e um contador.
// app/components/InteractivePanel.jsx
'use client';
import { useState } from 'react';
export default function InteractivePanel() {
const [showDetails, setShowDetails] = useState(false);
const [clicks, setClicks] = useState(0);
function handleToggleDetails() {
setShowDetails(!showDetails);
}
function handleIncrementClick() {
setClicks(clicks + 1);
}
return (
<div className="p-4 border rounded-lg shadow-md bg-white">
<h2 className="text-xl font-bold mb-2">Painel Interativo</h2>
<div className="flex items-center gap-4 mb-4">
<button
onClick={handleToggleDetails}
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
{showDetails ? 'Esconder Detalhes' : 'Mostrar Detalhes'}
</button>
<button
onClick={handleIncrementClick}
className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
>
Contar Cliques ({clicks})
</button>
</div>
{showDetails && (
<div className="mt-4 p-3 bg-gray-100 rounded">
<p className="text-gray-700">
Este é um exemplo de Client Component que gerencia seu próprio estado.
Você pode alternar a visibilidade de elementos e contar interações do usuário.
</p>
<p className="text-gray-600 mt-2">
O número de cliques é {clicks}.
</p>
</div>
)}
</div>
);
}Para usar este componente em uma página, você simplesmente o importa e o renderiza. Mesmo que a página seja um Server Component por padrão, ela pode importar e renderizar Client Components.
// app/page.jsx (Server Component por padrão)
import InteractivePanel from './components/InteractivePanel';
export default function HomePage() {
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24 bg-gray-50">
<h1 className="text-3xl font-bold mb-8">Bem-vindo ao Next.js 15!</h1>
<InteractivePanel /> {/* 👈 Renderizando um Client Component */}
<p className="mt-8 text-gray-500">
Esta parte da página é um Server Component, mas o painel acima é um Client Component.
</p>
</main>
);
}3. Exercícios Práticos: Mãos na Massa! 🚀
Agora é a sua vez de aplicar o que aprendemos. Você criará alguns Client Components para praticar interatividade e gerenciamento de estado.
Configuração Inicial
- Crie um novo projeto Next.js 15 (se ainda não tiver um):
npx create-next-app@latest my-interactive-app cd my-interactive-app - Abra o projeto no seu editor de código.
- Crie um diretório
app/componentspara seus componentes.
Desafios
Siga os passos abaixo para cada desafio. Crie um novo arquivo .jsx para cada componente dentro de app/components.
Desafio 1: Contador Simples 🔢
Crie um componente de contador que exiba um número e tenha dois botões: um para incrementar e outro para decrementar.
- Crie um arquivo
Counter.jsxemapp/components. - Adicione a diretiva
'use client'no topo. - Use
useStatepara gerenciar o estado do contador (valor inicial 0). - Renderize o valor atual do contador.
- Adicione dois botões, um com um manipulador
onClickpara incrementar o contador e outro para decrementar. - Importe e renderize seu
Countercomponente emapp/page.jsx.
💡 Dica para o Desafio 1
// app/components/Counter.jsx
'use client';
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div className="p-4 border rounded-lg shadow-md bg-white mb-4">
<h3 className="text-lg font-semibold">Contador Simples</h3>
<p className="text-2xl my-2">Valor: {count}</p>
<div className="flex gap-2">
<button
onClick={() => setCount(count + 1)}
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
Incrementar
</button>
<button
onClick={() => setCount(count - 1)}
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
>
Decrementar
</button>
</div>
</div>
);
}
// app/page.jsx
import Counter from './components/Counter';
export default function HomePage() {
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24 bg-gray-50">
<h1 className="text-3xl font-bold mb-8">Minha Aplicação Interativa</h1>
<Counter />
</main>
);
}Desafio 2: Alternador de Tema (Toggle Theme) 💡🌙
Crie um componente que alterna entre um "tema claro" e um "tema escuro".
- Crie um arquivo
ThemeToggler.jsxemapp/components. - Adicione a diretiva
'use client'no topo. - Use
useStatepara gerenciar o estado do tema (ex:'light'ou'dark', valor inicial'light'). - Renderize um botão que, ao ser clicado, alterna o tema. O texto do botão deve indicar o tema atual (ex: "Mudar para Escuro" ou "Mudar para Claro").
- Adicione uma div ou um parágrafo que mude sua cor de fundo e texto com base no tema atual (ex:
className={theme === 'dark' ? 'bg-gray-800 text-white' : 'bg-white text-gray-800'}). - Importe e renderize seu
ThemeTogglercomponente emapp/page.jsxabaixo doCounter.
💡 Dica para o Desafio 2
// app/components/ThemeToggler.jsx
'use client';
import { useState } from 'react';
export default function ThemeToggler() {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(theme === 'light' ? 'dark' : 'light');
};
const themeClasses = theme === 'dark'
? 'bg-gray-800 text-white'
: 'bg-white text-gray-800';
return (
<div className={`p-4 border rounded-lg shadow-md mb-4 ${themeClasses}`}>
<h3 className="text-lg font-semibold">Alternador de Tema</h3>
<p className="my-2">Tema atual: {theme}</p>
<button
onClick={toggleTheme}
className="bg-purple-500 hover:bg-purple-700 text-white font-bold py-2 px-4 rounded"
>
Mudar para {theme === 'light' ? 'Escuro' : 'Claro'}
</button>
<div className="mt-4 p-2 rounded" style={{ backgroundColor: theme === 'dark' ? '#333' : '#eee', color: theme === 'dark' ? 'white' : 'black' }}>
Este é um texto de exemplo para mostrar o tema.
</div>
</div>
);
}
// app/page.jsx
import Counter from './components/Counter';
import ThemeToggler from './components/ThemeToggler';
export default function HomePage() {
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24 bg-gray-50">
<h1 className="text-3xl font-bold mb-8">Minha Aplicação Interativa</h1>
<Counter />
<ThemeToggler />
</main>
);
}Desafio 3: Lista de Tarefas Simples (Todo List) ✅
Crie um componente básico de lista de tarefas onde você pode adicionar novas tarefas e exibi-las.
- Crie um arquivo
TodoList.jsxemapp/components. - Adicione a diretiva
'use client'no topo. - Use
useStatepara gerenciar:- Um array de tarefas (ex:
['Aprender Next.js', 'Fazer exercícios']). - O valor atual do input de nova tarefa.
- Um array de tarefas (ex:
- Renderize um input de texto e um botão "Adicionar Tarefa".
- O input deve ser um "input controlado" (seu valor deve ser vinculado ao estado).
- Ao clicar no botão "Adicionar Tarefa":
- Adicione o valor do input ao array de tarefas.
- Limpe o input.
- Exiba a lista de tarefas usando
mappara renderizar cada item. - Importe e renderize seu
TodoListcomponente emapp/page.jsxabaixo dos outros.
💡 Dica para o Desafio 3
// app/components/TodoList.jsx
'use client';
import { useState } from 'react';
export default function TodoList() {
const [todos, setTodos] = useState(['Aprender Next.js', 'Fazer exercícios']);
const [newTodo, setNewTodo] = useState('');
const handleAddTodo = () => {
if (newTodo.trim() !== '') {
setTodos([...todos, newTodo.trim()]);
setNewTodo(''); // Limpa o input
}
};
return (
<div className="p-4 border rounded-lg shadow-md bg-white w-full max-w-md">
<h3 className="text-lg font-semibold mb-2">Minha Lista de Tarefas</h3>
<div className="flex gap-2 mb-4">
<input
type="text"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
placeholder="Adicionar nova tarefa"
className="flex-grow border p-2 rounded-md"
/>
<button
onClick={handleAddTodo}
className="bg-teal-500 hover:bg-teal-700 text-white font-bold py-2 px-4 rounded"
>
Adicionar
</button>
</div>
<ul>
{todos.map((todo, index) => (
<li key={index} className="bg-gray-100 p-2 rounded-md mb-2">
{todo}
</li>
))}
</ul>
</div>
);
}
// app/page.jsx (atualizado)
import Counter from './components/Counter';
import ThemeToggler from './components/ThemeToggler';
import TodoList from './components/TodoList';
export default function HomePage() {
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24 bg-gray-50">
<h1 className="text-3xl font-bold mb-8">Minha Aplicação Interativa</h1>
<Counter />
<ThemeToggler />
<TodoList />
</main>
);
}4. Resumo e Próximos Passos 🎯
Parabéns! Você concluiu com sucesso a aula prática sobre Client Components.
Nesta aula, aprendemos:
- A importância da diretiva
'use client'para habilitar a interatividade no lado do cliente. - Como usar o hook
useStatepara gerenciar o estado local de um componente. - Como manipular eventos do usuário, como
onClickeonChange, para criar experiências dinâmicas. - A diferença fundamental entre Server Components e Client Components e como eles se complementam.
Você agora tem as ferramentas básicas para construir interfaces de usuário ricas e interativas no Next.js 15. Lembre-se de usar Client Components apenas quando a interatividade ou o acesso a APIs do navegador for estritamente necessário, mantendo o máximo possível no servidor para otimização.
Próximos Passos: No próximo módulo, exploraremos como os Client Components podem se comunicar com Server Components e como buscar dados de forma eficiente em ambos os ambientes. Fique ligado! ➡️