react-components-patterns

|

Installs: 0
Used in: 1 repos
Updated: 2d ago
$npx ai-builder add skill joaopelegrino/react-components-patterns

Installs to .claude/skills/react-components-patterns/

# React Components Patterns

> **Padrões de Componentes React para Plataforma B2B de treinamento técnico corporativo**
>
> **Versão:** 1.0.0
> **Última Atualização:** 2025-11-16
> **Target:** React 18.3.1 + Hooks
> **Projeto:** Plataforma B2B de treinamento técnico corporativo

---

## 📋 Índice

1. [Overview](#-overview)
2. [Quando Usar Esta Skill](#-quando-usar-esta-skill)
3. [Conceitos Fundamentais](#-conceitos-fundamentais)
4. [Padrões de Componentes](#-padrões-de-componentes)
5. [Exemplos Práticos do Projeto](#-exemplos-práticos-do-projeto)
6. [Antipadrões a Evitar](#-antipadrões-a-evitar)
7. [Troubleshooting](#-troubleshooting)
8. [Referências](#-referências)

---

## 🎯 Overview

Esta skill documenta todos os padrões de componentes React utilizados no **Plataforma B2B de treinamento técnico corporativo**, um sistema educacional com 17 componentes React, 227 módulos educacionais e 5 sistemas integrados de aprendizado.

**O que você vai aprender:**
- Como criar componentes funcionais reutilizáveis e testáveis
- Quando usar hooks vs. Context API vs. props drilling
- Padrões de composição (composition over inheritance)
- Estratégias de state management local
- Error boundaries e tratamento de erros
- Testes de componentes com Vitest e Testing Library

**Por que esta skill é importante:**
- ✅ **Consistência:** Garante que novos componentes seguem os mesmos padrões existentes
- ✅ **Manutenibilidade:** Reduz duplicação de código (meta: 25% → <10%)
- ✅ **Testabilidade:** Componentes funcionais são mais fáceis de testar
- ✅ **Performance:** Hooks permitem otimizações (useMemo, useCallback)
- ✅ **Escalabilidade:** Composição permite crescimento sustentável

---

## 📋 Quando Usar Esta Skill

### Cenário 1: Criando Novo Componente

**Situação:** Você precisa adicionar um novo componente (ex: `LessonCard`, `ProgressBar`, `QuizModal`)

**Use esta skill para:**
- Decidir entre functional component vs. class component (sempre functional!)
- Escolher hooks apropriados (useState, useEffect, custom hooks)
- Definir interface de props (destructuring, PropTypes/TypeScript)
- Estruturar JSX de forma legível e manutenível

**Exemplo de aplicação:**
```jsx
// ✅ Seguindo padrões da skill
function LessonCard({ title, duration, completed, onComplete, onClick }) {
  const [isExpanded, setIsExpanded] = useState(false);

  return (
    <div
      onClick={onClick}
      className="card"
    >
      <h3>{title}</h3>
      <span>{duration}</span>
      {completed && <span>✅</span>}
    </div>
  );
}
```

---

### Cenário 2: Refatorando Código Duplicado

**Situação:** Você identificou 5 componentes com lógica similar (ex: CLearningSystem, BashLearningSystem, RustLearningSystem)

**Use esta skill para:**
- Extrair lógica comum em hooks customizados
- Criar componentes genéricos (BaseLearningSystem)
- Aplicar composition patterns
- Reduzir de 800+ linhas duplicadas para componente reutilizável

**Exemplo de refatoração:**
```jsx
// ❌ Antes: 5 componentes com 800 linhas duplicadas
function CLearningSystem() {
  const [notes, setNotes] = useState('');
  const [progress, setProgress] = useState([]);
  // ... 160 linhas de lógica duplicada
}

// ✅ Depois: Hook customizado + componente genérico
function useCourseLogic(courseId) {
  const [notes, setNotes, saveStatus] = useAutoSaveNotes(courseId);
  const [progress, updateProgress] = useModuleProgress(courseId);
  return { notes, setNotes, saveStatus, progress, updateProgress };
}

function BaseLearningSystem({ courseId, courseData }) {
  const { notes, setNotes, progress, updateProgress } = useCourseLogic(courseId);
  // ... 40 linhas de lógica genérica
}
```

**Impacto:** Reduz manutenção de 5 arquivos para 1 componente + 2 hooks

---

### Cenário 3: Debugging Problemas de Estado

**Situação:** Componente re-renderiza infinitamente ou estado não atualiza corretamente

**Use esta skill para:**
- Entender fluxo unidirecional de dados (props down, events up)
- Diagnosticar problemas com useEffect dependencies
- Identificar mutações acidentais de estado
- Aplicar padrões de imutabilidade

**Exemplo de debug:**
```jsx
// ❌ Problema: Re-render infinito
useEffect(() => {
  setData(fetchData()); // ⚠️ Sem array de dependências!
});

// ✅ Solução: Dependências corretas
useEffect(() => {
  async function load() {
    const result = await fetchData();
    setData(result);
  }
  load();
}, []); // ✅ Roda apenas uma vez
```

---

## 💡 Conceitos Fundamentais

### 1. Functional Components com Hooks

**Princípio:** Sempre use functional components em vez de class components.

**Por quê:**
- ✅ Menos código boilerplate (sem constructor, bind)
- ✅ Hooks permitem reutilizar lógica (custom hooks)
- ✅ Mais fácil de testar (funções puras)
- ✅ Melhor suporte a TypeScript
- ✅ React team recomenda (class components legacy)

**Anatomia de um functional component:**

```jsx
import React, { useState, useEffect } from 'react';

/**
 * Card de área de estudo do Hub
 *
 * Props:
 * - titulo: string - Nome da área (ex: "Bash Shell Scripting")
 * - descricao: string - Descrição curta
 * - icone: string - Emoji representativo
 * - horas: number - Duração total em horas
 * - modulos: number - Quantidade de aulas
 * - onClick: function - Callback ao clicar
 */
function AreaCard({ titulo, descricao, icone, horas, modulos, onClick }) {
  // Estado local (se necessário)
  const [isHovered, setIsHovered] = useState(false);

  // Efeitos colaterais (se necessário)
  useEffect(() => {
    // Lógica que roda após render
    console.log(`AreaCard ${titulo} montado`);
  }, [titulo]); // Dependências

  // Event handlers
  const handleMouseEnter = () => setIsHovered(true);
  const handleMouseLeave = () => setIsHovered(false);

  // JSX Render
  return (
    <div
      onClick={onClick}
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
      className={`card ${isHovered ? 'card-hover' : ''}`}
    >
      <div className="card-header">
        <span className="icon">{icone}</span>
        <h3>{titulo}</h3>
      </div>

      <p className="description">{descricao}</p>

      <div className="meta">
        <span>📚 {modulos} aulas</span>
        <span>⏱️ {horas}h</span>
      </div>
    </div>
  );
}

export default AreaCard;
```

**Checklist de qualidade:**
- [ ] Usa function (não arrow function para componentes principais)
- [ ] Props destructured na assinatura
- [ ] Comentário JSDoc descrevendo props
- [ ] Event handlers com nomes descritivos (handleClick, handleChange)
- [ ] Hooks no topo do componente (antes de qualquer condicional)
- [ ] JSX legível (indentação correta, componentes pequenos)
- [ ] export default ao final

---

### 2. Composition Over Inheritance

**Princípio:** Componha componentes de partes menores, não herde de classes base.

**Por quê:**
- ✅ Mais flexível (mix and match)
- ✅ Menos acoplamento
- ✅ Reutilização via props e children
- ✅ Alinhado com filosofia React

**Exemplo Real: Breadcrumb Component**

```jsx
// ✅ Composition: Breadcrumb é composto de itens menores
function Breadcrumb({ items }) {
  return (
    <nav aria-label="Breadcrumb" className="breadcrumb">
      {items.map((item, index) => (
        <React.Fragment key={index}>
          <BreadcrumbItem {...item} />
          {index < items.length - 1 && <BreadcrumbSeparator />}
        </React.Fragment>
      ))}
    </nav>
  );
}

// Componente pequeno e reutilizável
function BreadcrumbItem({ label, icon, onClick, current }) {
  return (
    <button
      onClick={onClick}
      aria-current={current ? 'page' : undefined}
      className={`breadcrumb-item ${current ? 'current' : ''}`}
    >
      {icon && <span aria-hidden="true">{icon}</span>}
      {label}
    </button>
  );
}

// Separador visual
function BreadcrumbSeparator() {
  return <span aria-hidden="true" className="separator">›</span>;
}

// Uso no CLearningSystem
<Breadcrumb items={[
  { label: 'Hub', icon: '🏠', onClick: () => setView('hub') },
  { label: 'Curso de C', icon: '📖', onClick: () => setView('course') },
  { label: 'Aula 1.1', icon: '📝', current: true }
]} />
```

**Benefícios aplicados:**
- Breadcrumb testável independentemente
- BreadcrumbItem reutilizável em outros contextos
- Fácil adicionar novos tipos (BreadcrumbCollapse para mobile)

**Arquivo real:** `src/components/Breadcrumb.jsx:15-45`

---

### 3. Props vs. Context API

**Decisão:** Quando usar props drilling vs. Context API?

**Use Props quando:**
- ✅ Componente pai e filho próximos (1-2 níveis)
- ✅ Dados específicos de um componente
- ✅ Performance é crítica (props são mais rápidos)
- ✅ Relação clara parent → child

**Use Context API quando:**
- ✅ Dados globais (tema, usuário, idioma)
- ✅ Props drilling profundo (3+ níveis)
- ✅ Muitos componentes precisam do mesmo dado
- ✅ Evitar "prop tunneling"

**Exemplo Props (Caso Comum no Projeto):**

```jsx
// ✅ Props drilling aceitável (apenas 2 níveis)
function CLearningSystem() {
  const [selectedLesson, setSelectedLesson] = useState(null);

  return (
    <LessonList
      lessons={courseData.lessons}
      selectedId={selectedLesson?.id}
      onSelect={setSelectedLesson}
    />
  );
}

function LessonList({ lessons, selectedId, onSelect }) {
  return lessons.map(lesson => (
    <LessonItem
      key={lesson.id}
      lesson={lesson}
      isSelected={lesson.id === selectedId}
      onClick={() => onSelect(lesson)}
    />
  ));
}
```

**Exemplo Context (Planejado para Release 2.0):**

```jsx
// Context para tema (usado em 10+ componentes)
const ThemeContext = React.createContext('light');

function App() {
  const [theme, setTheme] = useState('light');

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <Header />
      <MainContent />
      <Footer />
    </ThemeContext.Provider>
  );
}

// Qualquer componente profundo pode acessar
function ThemeToggle() {
  const { theme, setTheme } = useContext(ThemeContext);

  return (
    <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
      {theme === 'light' ? '🌙' : '☀️'}
    </button>
  );
}
```

**Regra prática:** Start with props, migrate to Context when passing through 3+ components.

---

### 4. Controlled vs. Uncontrolled Components

**Controlled:** React controla o estado do input (recomendado)

**Uncontrolled:** DOM controla o estado (usar apenas se necessário)

**Exemplo Controlled (Caderno de Notas):**

```jsx
function NotesView({ courseId }) {
  const [notes, setNotes, saveStatus] = useAutoSaveNotes(courseId);

  return (
    <textarea
      value={notes} // ✅ Controlled: React é source of truth
      onChange={(e) => setNotes(e.target.value)}
      placeholder="Suas anotações..."
    />
  );
}
```

**Arquivo real:** `src/components/CNotesView.jsx:45-60`

**Por que controlled:**
- ✅ Estado sempre sincronizado
- ✅ Fácil validar e formatar input
- ✅ Auto-save funciona out of the box
- ✅ Testável (pode setar valor programaticamente)

---

### 5. Error Boundaries

**Princípio:** Componentes não devem crashar a aplicação inteira.

**Implementação:**

```jsx
// ErrorBoundary.jsx
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }

  componentDidCatch(error, errorInfo) {
    console.error('Error caught by boundary:', error, errorInfo);
    // Futuramente: enviar para Sentry
  }

  render() {
    if (this.state.hasError) {
      return (
        <div className="error-boundary">
          <h2>⚠️ Algo deu errado</h2>
          <p>{this.state.error?.message}</p>
          <button onClick={() => window.location.reload()}>
            Recarregar Página
          </button>
        </div>
      );
    }

    return this.props.children;
  }
}

// Uso no SistemaEducacionalCompleto
function App() {
  return (
    <ErrorBoundary>
      <HubView />
    </ErrorBoundary>
  );
}
```

**Arquivo real:** `src/components/ErrorBoundary.jsx:1-40`

**Nota:** Error boundaries **devem ser class components** (única exceção à regra functional-first)

---

## 🏗️ Padrões de Componentes

### Padrão 1: Presentational vs. Container

**Presentational (Dumb):** Apenas UI, recebe tudo via props

```jsx
// ✅ Presentational: apenas renderiza
function AreaCard({ titulo, icone, onClick }) {
  return (
    <div onClick={onClick} className="card">
      <span>{icone}</span>
      <h3>{titulo}</h3>
    </div>
  );
}
```

**Container (Smart):** Gerencia estado e lógica

```jsx
// ✅ Container: gerencia dados e lógica
function HubView() {
  const [areas, setAreas] = useState(studyAreas);
  const [filter, setFilter] = useState('');

  const filteredAreas = areas.filter(a =>
    a.titulo.toLowerCase().includes(filter.toLowerCase())
  );

  return (
    <div>
      <input
        value={filter}
        onChange={(e) => setFilter(e.target.value)}
        placeholder="Buscar área..."
      />
      {filteredAreas.map(area => (
        <AreaCard key={area.id} {...area} onClick={() => navigate(area.id)} />
      ))}
    </div>
  );
}
```

**Arquivo real:** `src/components/HubView.jsx:30-80`

**Benefício:** Presentational components são mais testáveis e reutilizáveis

---

### Padrão 2: Render Props (Avançado)

**Uso:** Compartilhar lógica entre componentes via função render

```jsx
// Componente que gerencia mouse position
function MouseTracker({ render }) {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const handleMove = (e) => setPosition({ x: e.clientX, y: e.clientY });
    window.addEventListener('mousemove', handleMove);
    return () => window.removeEventListener('mousemove', handleMove);
  }, []);

  return render(position);
}

// Uso
<MouseTracker render={({ x, y }) => (
  <p>Mouse em: {x}, {y}</p>
)} />
```

**Nota:** Padrão avançado, não usado atualmente no projeto (hooks são preferidos)

---

### Padrão 3: Higher-Order Components (HOC)

**Definição:** Função que recebe componente e retorna novo componente

```jsx
// HOC para adicionar loading state
function withLoading(Component) {
  return function WithLoadingComponent({ isLoading, ...props }) {
    if (isLoading) {
      return <div className="spinner">Carregando...</div>;
    }
    return <Component {...props} />;
  };
}

// Uso
const LessonListWithLoading = withLoading(LessonList);

<LessonListWithLoading
  isLoading={loading}
  lessons={lessons}
/>
```

**Nota:** HOCs são legado, hooks customizados são preferidos

---

## 🔧 Exemplos Práticos do Projeto

### Exemplo 1: AreaCard (Componente Mais Simples)

**Localização:** `src/components/AreaCard.jsx`

**Responsabilidade:** Card clicável de área de estudo no Hub

**Código completo:**

```jsx
function AreaCard({
  titulo,
  descricao,
  icone,
  horas,
  modulos,
  onClick
}) {
  return (
    <div
      onClick={onClick}
      className="
        bg-white rounded-lg p-6 shadow-lg
        hover:shadow-xl transition-shadow cursor-pointer
        border border-gray-200
      "
    >
      <div className="flex items-center gap-3 mb-3">
        <span className="text-4xl">{icone}</span>
        <h3 className="text-xl font-bold text-gray-800">{titulo}</h3>
      </div>

      <p className="text-gray-600 mb-4 line-clamp-2">{descricao}</p>

      <div className="flex gap-4 text-sm text-gray-500">
        <span>📚 {modulos} aulas</span>
        <span>⏱️ {horas}h</span>
      </div>
    </div>
  );
}

export default AreaCard;
```

**Análise do padrão:**
- ✅ Functional component puro (sem estado)
- ✅ Props destructured
- ✅ Tailwind classes inline
- ✅ Acessibilidade implícita (div clicável com onClick)
- ✅ Feedback visual (hover, transition)

**Uso no HubView:**

```jsx
<AreaCard
  titulo="Bash Shell Scripting"
  descricao="Domine o terminal"
  icone="💻"
  horas={16}
  modulos={16}
  onClick={() => setCurrentView('bash')}
/>
```

**Arquivo real:** `src/components/AreaCard.jsx:1-35`

---

### Exemplo 2: Breadcrumb (Componente com Estado e Acessibilidade)

**Localização:** `src/components/Breadcrumb.jsx`

**Responsabilidade:** Navegação hierárquica (Hub > Curso > Aula)

**Features:**
- ✅ WCAG 2.1 AA compliant
- ✅ Responsivo (colapsa em mobile)
- ✅ Sticky (sempre visível)
- ✅ Clicável (navegação entre níveis)

**Código (simplificado):**

```jsx
function Breadcrumb({ items }) {
  return (
    <nav
      aria-label="Breadcrumb"
      className="sticky top-0 z-10 bg-white shadow-md px-6 py-3"
    >
      <ol className="flex items-center gap-2">
        {items.map((item, index) => (
          <React.Fragment key={index}>
            <li>
              <button
                onClick={item.onClick}
                aria-current={item.current ? 'page' : undefined}
                className={`
                  flex items-center gap-2 px-3 py-1 rounded
                  ${item.current
                    ? 'bg-blue-100 text-blue-700 font-semibold'
                    : 'hover:bg-gray-100 text-gray-600'
                  }
                `}
              >
                {item.icon && <span aria-hidden="true">{item.icon}</span>}
                {item.label}
              </button>
            </li>

            {index < items.length - 1 && (
              <li aria-hidden="true" className="text-gray-400">
                ›
              </li>
            )}
          </React.Fragment>
        ))}
      </ol>
    </nav>
  );
}
```

**Arquivo real:** `src/components/Breadcrumb.jsx:1-95`

**Padrões aplicados:**
- ✅ Composition (Breadcrumb + BreadcrumbItem)
- ✅ Acessibilidade (aria-label, aria-current, semantic HTML)
- ✅ Responsividade (classes Tailwind)
- ✅ State management via props (items array)

**Validação US-061:** 13/13 critérios de aceite completos ✅

---

### Exemplo 3: CLearningSystem (Componente Complexo com Estado)

**Localização:** `src/components/CLearningSystem.jsx`

**Responsabilidade:** Sistema completo de aprendizado do Curso de C

**Estado gerenciado:**
- `currentView`: 'course' | 'lesson' | 'notes'
- `selectedLesson`: Lesson | null
- `completedLessons`: string[]
- `showFlashcards`: boolean

**Estrutura (simplificada):**

```jsx
function CLearningSystem({ onBack }) {
  // Estado de navegação
  const [currentView, setCurrentView] = useState('course');
  const [selectedLesson, setSelectedLesson] = useState(null);

  // Estado de progresso
  const [completedLessons, setCompletedLessons] = useState([]);

  // Estado de modals
  const [showFlashcards, setShowFlashcards] = useState(false);

  // Carregar progresso do localStorage ao montar
  useEffect(() => {
    const saved = localStorage.getItem('plataforma-b2b_progress_c');
    if (saved) {
      const { completedLessons } = JSON.parse(saved);
      setCompletedLessons(completedLessons);
    }
  }, []);

  // Handler de conclusão de aula
  const handleCompleteLesson = (lessonId) => {
    if (completedLessons.includes(lessonId)) return;

    const newCompleted = [...completedLessons, lessonId];
    setCompletedLessons(newCompleted);

    localStorage.setItem('plataforma-b2b_progress_c', JSON.stringify({
      completedLessons: newCompleted,
      lastUpdated: Date.now()
    }));
  };

  // Render condicional baseado em currentView
  if (currentView === 'notes') {
    return <CNotesView onBack={() => setCurrentView('course')} />;
  }

  if (currentView === 'lesson' && selectedLesson) {
    return (
      <div>
        <Breadcrumb items={[
          { label: 'Hub', icon: '🏠', onClick: onBack },
          { label: 'Curso de C', icon: '📖', onClick: () => setCurrentView('course') },
          { label: selectedLesson.title, icon: '📝', current: true }
        ]} />
        <LessonContent lesson={selectedLesson} />
        <button onClick={() => handleCompleteLesson(selectedLesson.id)}>
          ✅ Marcar como Concluída
        </button>
      </div>
    );
  }

  // View padrão: lista de aulas
  return (
    <div>
      <Breadcrumb items={[
        { label: 'Hub', icon: '🏠', onClick: onBack },
        { label: 'Curso de C', icon: '📖', current: true }
      ]} />

      <button onClick={() => setCurrentView('notes')}>
        📖 Estudar
      </button>

      <button onClick={() => setShowFlashcards(true)}>
        🎴 Flash Cards
      </button>

      {cCourseData.sections.map(section => (
        <Section key={section.sectionTitle}>
          <h2>{section.sectionTitle}</h2>
          {section.modules.map(lesson => (
            <LessonItem
              key={lesson.id}
              lesson={lesson}
              completed={completedLessons.includes(lesson.id)}
              onClick={() => {
                setSelectedLesson(lesson);
                setCurrentView('lesson');
              }}
            />
          ))}
        </Section>
      ))}

      {showFlashcards && (
        <FlashcardModal
          cards={cCourseData.flashcards}
          onClose={() => setShowFlashcards(false)}
        />
      )}
    </div>
  );
}
```

**Arquivo real:** `src/components/CLearningSystem.jsx:1-450`

**Padrões aplicados:**
- ✅ Multiple useState hooks (separação de concerns)
- ✅ useEffect para side effects (localStorage)
- ✅ Controlled components (estado sempre sincronizado)
- ✅ Conditional rendering (view switching)
- ✅ Event handlers descritivos
- ✅ Props drilling (onBack callback)

**Oportunidade de refatoração (US-043):**
- Extrair `useAutoSaveNotes` hook
- Extrair `useModuleProgress` hook
- Criar `BaseLearningSystem` componente genérico
- Reduzir 800 linhas duplicadas nos 5 sistemas

---

## ❌ Antipadrões a Evitar

### 1. Class Components (Exceto Error Boundaries)

**❌ Não fazer:**
```jsx
class AreaCard extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this); // ⚠️ Boilerplate
  }

  handleClick() {
    this.props.onClick();
  }

  render() {
    return <div onClick={this.handleClick}>{this.props.titulo}</div>;
  }
}
```

**✅ Fazer:**
```jsx
function AreaCard({ titulo, onClick }) {
  return <div onClick={onClick}>{titulo}</div>;
}
```

**Por quê:** Functional components são mais simples, testáveis e alinhados com React moderno.

---

### 2. Mutação Direta de Estado

**❌ Não fazer:**
```jsx
const [lessons, setLessons] = useState([]);

// ⚠️ Mutação direta
lessons.push(newLesson);
setLessons(lessons); // React pode não detectar mudança!
```

**✅ Fazer:**
```jsx
// ✅ Imutabilidade
setLessons([...lessons, newLesson]);
// ou
setLessons(prev => [...prev, newLesson]);
```

**Por quê:** React depende de referências para detectar mudanças. Mutação direta quebra isso.

---

### 3. useEffect sem Array de Dependências

**❌ Não fazer:**
```jsx
useEffect(() => {
  fetchData(); // ⚠️ Roda em todo render!
});
```

**✅ Fazer:**
```jsx
useEffect(() => {
  fetchData();
}, []); // ✅ Roda apenas uma vez (mount)

// Ou com dependências
useEffect(() => {
  fetchData(courseId);
}, [courseId]); // ✅ Roda quando courseId muda
```

**Por quê:** Sem array de dependências, effect roda em todo render (performance ruim).

---

### 4. Props Drilling Excessivo

**❌ Não fazer:**
```jsx
// 5 níveis de props drilling!
<App user={user}>
  <Layout user={user}>
    <Sidebar user={user}>
      <Menu user={user}>
        <UserInfo user={user} />
      </Menu>
    </Sidebar>
  </Layout>
</App>
```

**✅ Fazer:**
```jsx
// Context API para dados globais
const UserContext = createContext();

<UserContext.Provider value={user}>
  <App>
    <Layout>
      <Sidebar>
        <Menu>
          <UserInfo /> {/* acessa via useContext */}
        </Menu>
      </Sidebar>
    </Layout>
  </App>
</UserContext.Provider>
```

**Por quê:** Props drilling profundo é difícil de manter. Context é melhor para dados globais.

---

### 5. Inline Functions em Props (Performance)

**❌ Evitar em listas grandes:**
```jsx
{lessons.map(lesson => (
  <LessonItem
    key={lesson.id}
    onClick={() => handleClick(lesson.id)} // ⚠️ Nova função a cada render
  />
))}
```

**✅ Fazer (se performance crítica):**
```jsx
const handleLessonClick = useCallback((lessonId) => {
  // lógica
}, []);

{lessons.map(lesson => (
  <LessonItem
    key={lesson.id}
    onClick={handleLessonClick}
    lessonId={lesson.id}
  />
))}
```

**Por quê:** Inline functions criam nova referência a cada render, podem causar re-renders desnecessários.

**Nota:** No projeto atual (227 módulos), performance ainda OK. Otimizar quando necessário.

---

## 🚨 Troubleshooting

### Problema 1: "Cannot read property 'map' of undefined"

**Sintoma:**
```
Uncaught TypeError: Cannot read property 'map' of undefined
    at CLearningSystem.jsx:45
```

**Causa:** Array de dados ainda não carregado quando componente tenta renderizar.

**Solução:**
```jsx
// ❌ Erro
function LessonList({ lessons }) {
  return lessons.map(lesson => <LessonItem {...lesson} />);
}

// ✅ Solução: Early return ou default value
function LessonList({ lessons = [] }) {
  if (!lessons || lessons.length === 0) {
    return <p>Nenhuma aula disponível</p>;
  }

  return lessons.map(lesson => <LessonItem {...lesson} />);
}
```

**Arquivo:** Problema comum em `CLearningSystem.jsx:90`, `BashLearningSystem.jsx:85`

---

### Problema 2: "Warning: Each child in a list should have a unique key prop"

**Sintoma:**
```
Warning: Each child in a list should have a unique "key" prop.
```

**Causa:** Esqueceu de adicionar prop `key` ao mapear array.

**Solução:**
```jsx
// ❌ Erro
{lessons.map(lesson => (
  <LessonItem title={lesson.title} />
))}

// ✅ Solução: Usar ID único
{lessons.map(lesson => (
  <LessonItem key={lesson.id} title={lesson.title} />
))}

// ⚠️ Evitar usar index (exceto se lista estática)
{lessons.map((lesson, index) => (
  <LessonItem key={index} {...lesson} /> // OK apenas se lista não muda
))}
```

---

### Problema 3: "Maximum update depth exceeded"

**Sintoma:**
```
Error: Maximum update depth exceeded. This can happen when a component
repeatedly calls setState inside componentWillUpdate or componentDidUpdate.
```

**Causa:** useEffect sem dependências corretas causando loop infinito.

**Solução:**
```jsx
// ❌ Loop infinito
useEffect(() => {
  setData(data + 1); // ⚠️ Atualiza data, que causa re-render, que roda effect novamente
});

// ✅ Solução 1: Array de dependências vazio
useEffect(() => {
  setData(1);
}, []); // Roda apenas uma vez

// ✅ Solução 2: Dependências corretas
useEffect(() => {
  if (shouldUpdate) {
    setData(newValue);
  }
}, [shouldUpdate]); // Roda apenas quando shouldUpdate muda
```

---

### Problema 4: Estado não atualiza imediatamente

**Sintoma:**
```jsx
const [count, setCount] = useState(0);

const handleClick = () => {
  setCount(count + 1);
  console.log(count); // ⚠️ Ainda mostra valor antigo!
};
```

**Causa:** setState é assíncrono. Valor atualizado só disponível no próximo render.

**Solução:**
```jsx
// ✅ Usar functional update se depende do valor anterior
const handleClick = () => {
  setCount(prev => {
    console.log(prev); // Valor mais recente
    return prev + 1;
  });
};

// ✅ Ou usar useEffect para reagir a mudanças
useEffect(() => {
  console.log('Count atualizado:', count);
}, [count]);
```

---

### Problema 5: Component re-renderiza muitas vezes

**Sintoma:** Performance ruim, componente renderiza 10+ vezes por interação.

**Diagnóstico:** Usar React DevTools Profiler.

**Causas comuns:**
1. Inline functions em props (criar nova função a cada render)
2. Objetos/arrays criados inline em props
3. Context que muda frequentemente

**Soluções:**

```jsx
// ❌ Causa re-renders
function Parent() {
  return <Child config={{ foo: 'bar' }} />; // ⚠️ Novo objeto a cada render
}

// ✅ Solução: useMemo para valores computados
function Parent() {
  const config = useMemo(() => ({ foo: 'bar' }), []);
  return <Child config={config} />;
}

// ✅ Solução: React.memo para componentes puros
const Child = React.memo(function Child({ config }) {
  return <div>{config.foo}</div>;
});
```

**Arquivo útil:** `.claude/skills/vite-build-optimization/` para análise de bundle

---

## 📚 Referências

### Skills Relacionadas

- **[component-refactor](../component-refactor/SKILL.md)** - Padrões de refatoração para reduzir duplicação
- **[react-hooks-custom](../react-hooks-custom/SKILL.md)** - Criação de hooks customizados
- **[system-state-management](../system-state-management/SKILL.md)** - State management avançado
- **[testing-strategy-vitest](../testing-strategy-vitest/SKILL.md)** - Testes de componentes React

### Documentação Técnica

- **[docs/tecnico/architecture/01-visao-geral-arquitetura.md](../../docs/tecnico/architecture/01-visao-geral-arquitetura.md)** - Arquitetura completa (4 camadas)
- **[docs/conceitual/01-visao-geral/00-definicoes-principais.md](../../docs/conceitual/01-visao-geral/00-definicoes-principais.md)** - Glossário e nomenclatura

### Código Real do Projeto

**Componentes Exemplares:**
- `src/components/AreaCard.jsx` - Componente simples e puro
- `src/components/Breadcrumb.jsx` - Acessibilidade e composição
- `src/components/CLearningSystem.jsx` - Componente complexo com estado
- `src/components/ErrorBoundary.jsx` - Error handling

**Hooks Customizados (Planejados):**
- `src/hooks/useAutoSaveNotes.js` - Auto-save para notas
- `src/hooks/useModuleProgress.js` - Progresso de aulas

### Recursos Externos

- **[React Docs - Components and Props](https://react.dev/learn/components-and-props)**
- **[React Docs - Hooks](https://react.dev/reference/react)**
- **[React Patterns](https://reactpatterns.com/)** - Catálogo de padrões
- **[Kent C. Dodds - Application State Management](https://kentcdodds.com/blog/application-state-management-with-react)** - Props vs Context

---

## 📝 Arquivos Auxiliares

Esta skill possui 3 arquivos auxiliares detalhados:

1. **[functional-components.md](./auxiliary/functional-components.md)** - Guia completo de functional components
2. **[hooks-guide.md](./auxiliary/hooks-guide.md)** - useState, useEffect, custom hooks em profundidade
3. **[composition-patterns.md](./auxiliary/composition-patterns.md)** - Padrões avançados de composição

---

**📍 Você está em:** `.claude/skills/react-components-patterns/SKILL.md`
**📅 Criado em:** 2025-11-16
**👤 Mantido por:** João Pelegrino + Claude Code
**🎯 Uso:** Referência para criar e manter componentes React no projeto
**🔄 Última auditoria:** 2025-11-16
**📊 Auto-discovery score:** TBD (testar após criação)

Quick Install

$npx ai-builder add skill joaopelegrino/react-components-patterns

Details

Type
skill
Slug
joaopelegrino/react-components-patterns
Created
6d ago