React: useEffect e o ciclo de vida

Última atualização: 2026-10-26

Se useState é a “memória” de um componente, então useEffect é sua “câmera” — ela permite que você faça algumas coisas adicionais depois que o componente já foi renderizado na tela (“depois de tirar a foto”): solicitar dados, assinar eventos, manipular o DOM e definir temporizadores.


1. O que você vai aprender



2. A história de uma página de carregamento de dados

(1) Problema: As solicitações de dados causam um laço infinito

Alice quer solicitar os dados do usuário quando o componente for carregado, então ela escreve o seguinte:

JSX
function UserProfile({ userId }) {
  const [user, setUser] = React.useState(null)

  // ❌ Error:Requesting data directly within a component's function body
  fetch('/api/users/' + userId)
    .then(res => res.json())
    .then(data => setUser(data))

  return <div>{user?.name}</div>
}
▶ Experimente

Pergunta:

  1. fetch → setUser(data) → Reexibição do componente
  2. Renderizar novamente → Executar fetch novamente → Definir o status novamente → Renderizar novamente...
  3. Loop infinito! A página está travada.

Alice precisa de uma maneira de “executar apenas uma vez quando o componente for montado”.

(2) Solução com useEffect

JSX
import { useState, useEffect } from 'react'

function UserProfile({ userId }) {
  const [user, setUser] = useState(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)

  // ✅ Correct:useEffect + Empty array of dependencies
  useEffect(() => {
    fetch('/api/users/' + userId)
      .then(res => {
        if (!res.ok) throw new Error('Failed to load')
        return res.json()
      })
      .then(data => {
        setUser(data)
        setLoading(false)
      })
      .catch(err => {
        setError(err.message)
        setLoading(false)
      })
  }, [userId])  // Only at userId Re-execute when changes occur

  if (loading) return <p>Loading......</p>
  if (error) return <p>Error:{error}</p>
  return <h1>{user.name}</h1>
}
▶ Experimente

Benefícios: useEffect Separa os efeitos colaterais da lógica de renderização — a renderização é responsável apenas pela interface do usuário, e os efeitos colaterais são executados após a conclusão da renderização.



3. Noções básicas sobre o useEffect

(1) Sintaxe básica

JSX
useEffect(() => {
  // Side Effect Logic(Execute after rendering is complete)
  console.log('Component rendering complete!')
  
  return () => {
    // Cleanup Function(Run before unloading or re-executing a component)
    console.log('Addressing Side Effects')
  }
}, [Dependency Array])
▶ Experimente
Seção Descrição
() => { ... } Efeito colateral da chamada de retorno, executada após a renderização ter sido finalizada na tela
return () => { ... } Função de limpeza; é executada antes da reexecução devido à desinstalação de um componente ou a alterações nas dependências
[dependencies] Um array que controla quando o useEffect é executado novamente
100%
graph TB
    A[Component Rendering] --> B[React Submit DOM Update]
    B --> C[useEffect Callback Execution]
    C --> D{Changes in Dependencies?}
    D -->|is | E[Execute the previous cleanup function]
    E --> F[Re-execute useEffect]
    D -->|No| G[Skip]
    F --> H{Component Uninstallation?}
    G --> H
    H -->|is | I[Execute the cleanup function]
    
    style C fill:#61dafb,color:#000
    style I fill:#ff6b6b,color:#fff

(2) Três padrões para matrizes de dependências

JSX
// Pattern 1:Empty array [] — Execute only once when mounted
useEffect(() => {
  console.log('Component Mounting')
  fetch('/api/init').then(setData)
}, [])  // Never re-execute

// Pattern 2:Dependencies [a, b] — Re-execute when dependencies change
useEffect(() => {
  console.log('userId It has changed.:', userId)
  fetch('/api/users/' + userId).then(setUser)
}, [userId])  // userId Re-execute when changes occur

// Pattern 3:No array dependencies — Executed on every render
useEffect(() => {
  console.log('Executed on every render')
})  // Note:No array!
▶ Experimente
Conjunto de dependências Tempo de execução Casos de uso
[] Executado apenas uma vez ao montar Inicializar solicitações de dados e assinaturas
[dep1, dep2] Ao ser montado + quando as dependências mudam Os dados mudam com os parâmetros
Inúmeros conjuntos Após cada renderização Raramente usado (geralmente substituído por outros métodos)


4. Funções de limpeza

As funções de limpeza são fundamentais para evitar vazamentos de memória e condições de corrida.

(1) Zerar o cronômetro

▶ Experimente

(2) Limpeza dos ouvintes de eventos

▶ Experimente

(3) Cancelamento da solicitação (para evitar condições de corrida)

JSX
function SearchResults({ query }) {
  const [results, setResults] = useState([])

  useEffect(() => {
    let cancelled = false

    fetch('/api/search?q=' + query)
      .then(res => res.json())
      .then(data => {
        // ✅ If the component has been uninstalled or query Has changed,Do not update status
        if (!cancelled) {
          setResults(data)
        }
      })

    // ✅ Cleanup:Uncheck
    return () => {
      cancelled = true
    }
  }, [query])

  return <div>{results.map(r => <p key={r.id}>{r.name}</p>)}</div>
}
// When users quickly switch search terms:
// 1. Search"A" → useEffect Execute
// 2. Search"AB" → Clear Function Markers cancelled=true → useEffect Re-execute
// 3. Search"A" the response came back → cancelled=true → Do not update status(Avoid flashing) 
▶ Experimente

Saída:

TEXT
Subtítulo: "useEffect Life Cycle Demonstration". Estado: count, logs. Efeitos colaterais via useEffect. Atualização por temporizador

▶ Exemplo: Demonstração abrangente das funções de limpeza

▶ Experimente

Saída:

TEXT
A contagem aumenta +1 a cada 2s, a barra de espaço soma +5. Log: "⏱️ Timer started". Na desmontagem → "⏱️ Timer cleared", "⌨️ Keyboard listening removed"


5. Mapeando o useEffect ao ciclo de vida do componente

Na era dos componentes de classe, havia três ganchos de ciclo de vida; na era dos Hooks, useEffect — um único gancho faz o trabalho de três:

100%
graph LR
    subgraph "The Lifecycle of Class Components"
        A1[componentDidMount] --> A2[componentDidUpdate]
        A2 --> A3[componentWillUnmount]
    end
    
    subgraph "Hooks 's  useEffect"
        B1["useEffect(fn, [])<br/>Execute when mounted"] 
        B2["useEffect(fn, [dep])<br/>Mount + run when deps change"]
        B3["return cleanup<br/>Clean up before uninstalling or rerunning"]
    end

Saída:

TEXT
Subtítulo: "useEffect Life Cycle Demonstration". Exibe: "useEffect Life Cycle Demonstration". Estado: count (setter: setCount), logs (setter: setLogs). useEffect gerencia efeitos colaterais. Atualizações baseadas em temporizador
Ciclo de vida dos componentes de classe useEffect correspondente
componentDidMount useEffect(() => { ... }, [])
componentDidUpdate useEffect(() => { ... }) (inúmeros conjuntos) ou useEffect(() => { ... }, [dep])
Funções de limpeza em componentWillUnmount useEffect(() => { return () => { ... } }, [])

▶ Exemplo: Como usar um único useEffect para lidar com várias fases do ciclo de vida

JSX
// ============================================
// Example:Chat Room Component——One useEffect Covering the Entire Lifecycle
// ============================================

function ChatRoom({ roomId }) {
  const [messages, setMessages] = useState([])

  useEffect(() => {
    // === componentDidMount + componentDidUpdate ===
    console.log(`🟢 Connect to the chat room:${roomId}`)
    const connection = createConnection(roomId)
    connection.connect()

    connection.on('message', (msg) => {
      setMessages(prev => [...prev, msg])
    })

    // === componentWillUnmount + Clean Up Before Re-Execution ===
    return () => {
      console.log(`🔴 Leave the chat room:${roomId}`)
      connection.disconnect()
    }
  }, [roomId])  // roomId When things change:Disconnect the old connection → Join a New Chat Room

  return (
    <div>
      <h3>Chat Room:{roomId}</h3>
      {messages.map((msg, i) => <p key={i}>{msg}</p>}
    </div>
  )
}

// Usage
<ChatRoom roomId="general" />     // Connect general Chat Room
<ChatRoom roomId="random" />      // roomId Changes → Disconnect general → Connect random
// When a component is unloaded → Disconnect the current connection(Execution of the cleanup function)
▶ Experimente

Saída:

TEXT
Montagem: console "🟢 Connected to chat room: general". Mudança de roomId: "🔴 Left: general" → "🟢 Connected: random". Desmontagem: desconecta.


6. useEffect x useLayoutEffect

O React oferece dois hooks com “efeitos colaterais”, que diferem em termos de tempo de execução.

100%
graph LR
    A[Trigger Rendering] --> B[Computational Virtual DOM]
    B --> C[DOM Update]
    C --> D{useLayoutEffect?}
    D -->|is | E[Execute in parallel<br/>Blocking Browser Rendering]
    D -->|No| F[Browser rendering complete]
    F --> G[useEffect Asynchronous Execution]
    
    style E fill:#faad14,color:#000
    style G fill:#52c41a,color:#fff
Hook Tempo de execução Bloqueia a renderização Casos de uso
useEffect Após a renderização do navegador (assíncrona) ❌ 90% dos cenários: solicitações de dados, ouvintes de eventos, registro em log
useLayoutEffect Após as atualizações do DOM, antes da renderização do navegador (síncrono) ✅ Medir as dimensões do DOM, ler o layout, sincronizar animações
▶ Experimente

Regras: Comece digitando useEffect; só mude para useLayoutEffect se encontrar problemas de “cintilação” ou “instabilidade no layout”.



7. Exemplo completo: Painel de dados

▶ Expandir exemplo executável (98 linhas)
▶ Experimente

Saída:

TEXT
🟢 Connect to the chat room:...
🔴 Leave the chat room:...

Resultado esperado: Um painel de dados contendo 4 cartões de estatísticas, um botão para ativar/desativar a atualização em tempo real, um indicador do tamanho da janela e a hora da última atualização.


▶ Exemplo 3: Utilização do useEffect para implementar a sincronização de dados em tempo real

JSX
function LiveSearch({ apiBase }) {
  const [query, setQuery] = useState('')
  const [results, setResults] = useState([])
  const [loading, setLoading] = useState(false)

  useEffect(() => {
    if (!query.trim()) { setResults([]); return }
    setLoading(true)
    const controller = new AbortController()
    fetch(`${apiBase}/search?q=${encodeURIComponent(query)}`, { signal: controller.signal })
      .then(res => res.json())
      .then(data => { setResults(data); setLoading(false) })
      .catch(err => { if (err.name !== 'AbortError') setLoading(false) })
    return () => controller.abort()
  }, [query, apiBase])

  return (
    <div style={{ maxWidth: 400, margin: '0 auto' }}>
      <input value={query} onChange={e => setQuery(e.target.value)}
        placeholder="Search users..." style={{ width: '100%', padding: 8, borderRadius: 4, marginBottom: 8 }} />
      {loading && <p>Searching...</p>}
      <ul style={{ listStyle: 'none', padding: 0 }}>
        {results.map(r => <li key={r.id} style={{ padding: 4, borderBottom: '1px solid #f0f0f0' }}>{r.name}</li>)}
      </ul>
    </div>
  )
}
▶ Experimente

Saída:

TEXT
useEffect gerencia efeitos colaterais

Saída:

TEXT
Exibe: "Searching...". Estado: query (setter: setQuery), results (setter: setResults), loading (setter: setLoading). Entrada: Search users.... Lista: {r.name}. useEffect gerencia efeitos colaterais. Busca assíncrona de dados / estados de carregamento

▶ Exemplo 4: useEffect para monitorar alterações no tamanho da janela

▶ Experimente

Saída:

TEXT
useEffect gerencia efeitos colaterais

Saída:

TEXT
Exibe: "📱 Single column layout". Estado: size (setter: setSize). useEffect gerencia efeitos colaterais

▶ Exemplo 5: useEffect + localStorage para persistir o estado

▶ Experimente

Saída:

TEXT
A lista de tarefas é salva no localStorage. Adicione tarefas, clique para alternar (tachado). Atualizar a página → as tarefas persistem. Enter para adicionar.

❓ Perguntas Frequentes

P Qual é a diferença entre useEffect e escrever código diretamente dentro de um componente?
R O código dentro de useEffect é executado somente após a conclusão da renderização, portanto não bloqueia a interface do usuário. O código escrito diretamente no corpo da função de um componente é executado durante a renderização (o que bloqueia a renderização) e é executado a cada renderização. A principal diferença é que useEffect permite que você execute uma limpeza quando o componente é desmontado.
P Por que a função de limpeza em useEffect é executada antes de cada nova execução?
R Essa é uma garantia de design do React — ela evita “condições de corrida” causadas por alterações nas dependências. Por exemplo, quando o userId é alterado, a solicitação antiga é limpa antes que uma nova seja iniciada, garantindo que os novos dados substituam os antigos. Se a nova solicitação substituísse a antiga diretamente, poderia ocorrer um bug em que “a solicitação antiga fosse concluída primeiro → os dados fossem revertidos”.
P Qual é a ordem de execução de vários ganchos useEffect?
R Eles são executados na ordem em que aparecem no código. Portanto, você deve colocar os hooks useEffect que lidam com a “inicialização” (como o carregamento de dados) primeiro, e aqueles que lidam com a “resposta a alterações” (como a sincronização do estado com localStorage) por último. Durante uma única renderização, todos os hooks useEffect são executados em sequência.
P Qual é a diferença entre useEffect e useLayoutEffect?
R A diferença está no momento em que são executados: useEffect é executado de forma assíncrona após a atualização do DOM (após a renderização do navegador), de modo que os usuários não percebam nenhuma oscilação; useLayoutEffect é executado de forma síncrona após a atualização do DOM (antes da renderização do navegador), permitindo que você modifique o DOM e, em seguida, faça com que o navegador o renderize. Use useEffect em 99% dos casos. Use useLayoutEffect apenas quando você “precisar fazer cálculos com base no layout do DOM e, em seguida, modificar o DOM de forma síncrona” (como ajustar a posição de um elemento após medir suas dimensões).

📖 Resumo


📝 Exercícios

  1. Problema básico (Dificuldade ⭐): Crie um componente PageTitle. Use useEffect para alterar o título da página para “Bem-vindo ao meu site” quando o componente for montado e restaurá-lo ao título original quando for desmontado.
  2. Exercício avançado (Dificuldade ⭐⭐): Crie um componente OnlineStatus que utilize useEffect para monitorar eventos provenientes de navigator.onLine e online/offline e exiba o status da rede em tempo real.
  3. Desafio (Dificuldade: ⭐⭐⭐): Crie um hook personalizado chamado usePolling que aceite os parâmetros url e interval, faça a consulta à interface uma vez a cada intervalo de milissegundos e retorne { data, isLoading, error }. O hook deve permitir pausar e retomar a consulta.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%