Fundamentos do Next.js 15

0/26 aulas0%
pratica

Interatividade e Gerenciamento de Estado com Client Components

Aprenda sobre interatividade e gerenciamento de estado com client components

40 min
Aula 4 de 5

🚀 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

  1. 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
  2. Abra o projeto no seu editor de código.
  3. Crie um diretório app/components para 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.jsx em app/components.
  • Adicione a diretiva 'use client' no topo.
  • Use useState para gerenciar o estado do contador (valor inicial 0).
  • Renderize o valor atual do contador.
  • Adicione dois botões, um com um manipulador onClick para incrementar o contador e outro para decrementar.
  • Importe e renderize seu Counter componente em app/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.jsx em app/components.
  • Adicione a diretiva 'use client' no topo.
  • Use useState para 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 ThemeToggler componente em app/page.jsx abaixo do Counter.
💡 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.jsx em app/components.
  • Adicione a diretiva 'use client' no topo.
  • Use useState para gerenciar:
    • Um array de tarefas (ex: ['Aprender Next.js', 'Fazer exercícios']).
    • O valor atual do input de nova tarefa.
  • 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 map para renderizar cada item.
  • Importe e renderize seu TodoList componente em app/page.jsx abaixo 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 useState para gerenciar o estado local de um componente.
  • Como manipular eventos do usuário, como onClick e onChange, 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! ➡️

© 2025 Escola All Dev. Todos os direitos reservados.

Interatividade e Gerenciamento de Estado com Client Components - Fundamentos do Next.js 15 | escola.all.dev.br