Aprenda a usar React Hooks com essas 7 dicas

React

Introdução

Os React Hooks são tipo uma super ferramenta para todos os devs React, mas especialmente para quem tá começando ou já tá na pegada intermediária. Eles são uma das principais ferramentas pra melhorar a legibilidade, reutilização e manutenção do seu código.

Pensa comigo: é como trocar blocos de construção convencionais por uns feitos sob medida, que deixam sua aplicação mais estável e organizada. E é isso que os React Hooks podem fazer por você!

Tem um exemplo completo aqui de como fazer uma lista de tarefas usando React Hooks. Dá uma conferida!

O que são React Hooks e por que são úteis?

Os React Hooks vieram pra facilitar a vida na hora de gerenciar o estado e os efeitos colaterais nos componentes funcionais do React. A ideia era simplificar e deixar nosso código mais limpo, mais fácil de ler, reutilizar e manter.

Antes dos Hooks, a parada era usar componentes de classe, que eram meio que como um quebra-cabeça – dava pra resolver, mas às vezes era meio chato, especialmente quando você tinhas muitas peças. Era tipo procurar uma agulha no palheiro de código.

Por exemplo, quer um contador que aumenta quando você clica num botão? Suuuuper fácil com o useState. E quando você quer que algo aconteça sempre que esse contador muda, é só usar o useEffect. Dá uma olhada na diferença entre fazer isso com React Hooks e componentes de classe.

// Sem React Hooks
class Contador extends React.Component {
    state = {
        count: 0,
    }

    componentDidMount() {
        document.title = `Contagem: ${this.state.count}`
    }

    componentDidUpdate(prevProps, prevState) {
        if (prevState.count !== this.state.count) {
            document.title = `Contagem: ${this.state.count}`
        }
    }

    render() {
        return (
            <div>
                <p>Count: {this.state.count}</p>
                <button onClick={() => this.setState({ count: this.state.count + 1 })}>Incrementar</button>
            </div>
        )
    }
}

// Com React Hooks
function Counter() {
    const [count, setCount] = useState(0)

    useEffect(() => {
        document.title = `Contagem: ${count}`
    }, [count])

    return (
        <div>
            <p>Contagem: {count}</p>
            <button onClick={() => setCount(count + 1)}>Incrementar</button>
        </div>
    )
}

React Hooks, especialmente o useState e o useEffect, são como uma caixa de ferramentas que deixa a experiência de desenvolvimento React bem mais produtiva. Eles te incentivam a escrever um código mais organizado e que dá pra reutilizar, resultando em aplicativos mais limpos e fáceis de manter.

Ah, e os React Hooks incluem também o useRef, useContext, useReducer e mais uns outros. Dá uma conferida na lista completa. Esses hooks podem ser usados pra gerenciar o estado, fazer uns truques com efeitos colaterais e acessar dados do contexto de várias maneiras.

  1. Use useState apenas para gerenciar o estado que é essencial para seu componente

O useState é um poderoso React Hook que te permite controlar o estado nos componentes funcionais. Dá pra usar pra ficar de olho em tudo que muda com o tempo, tipo o valor de um campo de formulário, a pontuação num jogo ou a lista de coisas no carrinho de compras.

Cada vez que você usa o useState, você tá criando uma variável de estado nova. Isso pode acumular e deixar teus componentes lentos e difíceis de entender, então saiba usar da maneira correta, criando apenas states essenciais para o seu componente.

Faça:

import React, { useState } from 'react';

const Contador = () => {
    const [contador, setContador] = useState(0); // Estado essencial: contador

    const handleIncrement = () => {
        setContador(prevContador => prevContador + 1);
    };

    return (
        <div>
            <p>Contagem: {contador}</p>
            <button onClick={handleIncrement}>Incrementar</button>
        </div>
    );
};

export default Contador;

Aqui a gente tá usando o useState pra gerenciar o estado essencial (contador) que é necessário pro componente, que é o jeito certo.

Manter apenas o estado essencial no teu componente ajuda a manter teu código organizado e fácil de manter. Também facilita entender como o teu componente funciona e como ele muda com o tempo.

Não faça:

import React, { useState } from 'react';

const PerfilUsuario = () => {
    const [logado, setLogado] = useState(false);  // Estado não essencial: logado

    const handleLogin = () => {
        setLogado(true);
    };

    const handleLogout = () => {
        setLogado(false);
    };

    return (
        <div>
            <p>O usuário está logado: {logado ? 'Sim' : 'Não'}</p>
            <button onClick={handleLogin}>Entrar</button>
            <button onClick={handleLogout}>Sair</button>
        </div>
    );
};

export default PerfilUsuario;

Já nesse componente, o estado logado usando React Hooks é considerado não essencial porque não está diretamente relacionado à funcionalidade principal ou propósito do componente UserProfile. O objetivo desse componente é mostrar as informações do perfil do usuário, o foco principal não é gerenciar o status de login do usuário.

Fica de olho em estados não essenciais usando React Hooks no teu componente, porque isso pode complicar e dificultar a leitura do teu código. Além disso, pode causar problemas de desempenho, especialmente quando temos uma grande quantidade de estados.

  1. Use useState pra controlar um estado imutável

Pense em um state como algo que não pode ser alterado diretamente. Isso quer dizer que você nunca deve alterar diretamente a variável de estado. Em vez disso, sempre que quiser fazer uma alteração, crie uma cópia nova dessa variável. Isso ajuda a manter a confiabilidade dos seus dados e evita mudanças inesperadas, garantindo que o seu programa funcione como esperado.

Faça:

import React, { useState } from 'react';

const ContadorImutavel = () => {
    const [contador, setContador] = useState(0);  // Estado imutável: contador

    const handleIncremento = () => {
        // Atualize o contador de forma imutável usando a função setContador
        setContador(prevContador => prevContador + 1);
    };

    return (
        <div>
            <p>Contagem: {contador}</p>
            <button onClick={handleIncremento}>Incrementar</button>
        </div>
    );
};

export default ContadorImutavel;

Nesse exemplo, a gente tá usando o setContador para atualizar o contador que não deve ser mudado diretamente. Isso segue as regras do React, deixando tudo mais organizado e confiável. Manter os estados imutáveis também facilita entender e encontrar erros no teu código.

Não Faça:

const ContadorMutavel = () => {
    const [contador, setContador] = useState(0);

    useEffect(() => {
        setContador(contador + 1);
    });

    return (
        <div>
            <p>Contagem: {contador}</p>
        </div>
    );
};

A razão de não ser correto atualizar as variáveis de estado diretamente no useEffect, por exemplo, é porque isso pode gerar loops infinitos.

Nesse exemplo, o useEffect vai ser acionado depois de cada renderização, e ele vai incrementar a variável de estado de contagem (contador), isso quer dizer que a variável de estado vai ser incrementada continuamente, criando um loop infinito.

  1. Use useState pra organizar teu estado de uma maneira normalizada

Significa dizer que você deve organizar o estado(s) de forma padronizada e arrumada. Ao invés de amontoar todos os dados num monte só, divida em pedacinhos menores e bem organizados. Cada pedaço de dado com seu identificador único, o que facilita encontrar, atualizar ou trabalhar com eles.

Faça:

import React, { useState } from 'react';

const ProdutosNormalizados = () => {
  const [produtos, setProdutos] = useState({
    1: { id: 1, nome: 'Produto A' },
    2: { id: 2, nome: 'Produto B' }
  });  // Estado normalizado: produtos

  return (
    <div>
      <ul>
        {Object.values(produtos).map(produto => (
          <li key={produto.id}>{produto.nome}</li>
        ))}
      </ul>
    </div>
  );
};

export default ProdutosNormalizados;

Um exemplo de como usar o useState e deixar nossos dados organizados. Cada produto tem seu próprio ID especial, o que facilita encontrar e mexer neles. Assim, a gente consegue buscar nossos produtos e fazer mudanças quando preciso. É como ter uma caixa de ferramentas bem organizada onde cada ferramenta tem seu lugar – dá pra achar a ferramenta certa rapidão.

Não Faça:

import React, { useState } from 'react';

const ProdutosNaoNormalizados = () => {
  const [produtos, setProdutos] = useState([
    { id: 1, nome: 'Produto A' },
    { id: 2, nome: 'Produto B' }
  ]);  // Estado não normalizado: produtos

  return (
    <div>
      <ul>
        {produtos.map(produto => (
          <li key={produto.id}>{produto.nome}</li>
        ))}
      </ul>
    </div>
  );
};

export default ProdutosNaoNormalizados;

Um state “desnormalizado” usando React Hooks significa que os dados não estão organizados de maneira eficiente. Nesse exemplo, a gente tá usando o useState pra gerenciar uma lista de produtos também, mas aqui cada produto é representado como um objeto dentro de um array e não existe um identificador para esse produto.

Se tu tem muitos produtos na lista, usar um estado desorganizado com React Hooks pode dificultar achar e atualizar produtos específicos. Também pode causar problemas de desempenho, especialmente se tu tá mostrando uma lista com todos os teus produtos.

  1. Use o useEffect apenas para efeitos colaterais.

useEffect não foi feito pra atualizar o estado ou fazer o componente aparecer na tela. Pense nele como a ferramenta que cuida das coisas que rolam nos bastidores enquanto teu componente faz sua mágica. Então, se precisar buscar dados de uma API ou ficar de olho em atualizações, é aí que o useEffect entra em ação.

Faça:

import React, { useEffect } from 'react';

const SubscriptionManager = () => {
  useEffect(() => {
    // Simulando uma assinatura de um evento
    const subscription = someEventEmitter.on('event', () => {
      // Lidar com o evento
      console.log('Evento recebido!');
    });

    // Limpeza: Cancelar a assinatura quando o componente é desmontado
    return () => {
      someEventEmitter.off('event', subscription);
    };
  }, []); // Array de dependências vazio

  return <p>Gerenciador de Assinaturas</p>;
};

export default SubscriptionManager;

Nesse exemplo, a gente ta usando o useEffect pra gerenciar uma inscrição num evento. É um cenário típico de efeito colateral, onde a gente se inscreve num evento e garante cancelar a inscrição quando o componente é desmontado.

Não Faça:

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

const ExemploInvalido = () => {
  const [mensagem, setMensagem] = useState('Mensagem inicial');

  useEffect(() => {
    // Isso está incorreto: o efeito está atualizando o estado e causando re-renderizações
    setMensagem('Mensagem atualizada');
  }, []); // Array de dependências vazio

  return <p>{mensagem}</p>;
};

export default ExemploInvalido;

Aqui a gente tá usando o useEffect pra atualizar o estado do componente diretamente. Isso é uma maneira errada de usar o useEffect, porque ele é feito pra lidar com efeitos colaterais, não pra causar novas renderizações ou atualizar o estado diretamente. Sempre use o useEffect só pra tarefas relacionadas aos efeitos colaterais.

  1. Especifique as dependências

O useEffect pega uma lista de dependências como argumento, e ele só vai rodar quando alguma dessas dependências mudar. Isso significa que a gente avisa o React sobre coisas específicas (como variáveis ou pedaços de dados) que ele precisa ficar de olho. Se alguma dessas coisas mudar, o React sabe que tem que fazer algo.

Faça:

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

const ExemploComDependencias = () => {
  const [contador, setContador] = useState(0);
  const [multiplicador, setMultiplicador] = useState(1);

  useEffect(() => {
    // Este efeito será executado sempre que 'contador' ou 'multiplicador' mudarem
    console.log('Efeito executado! Contador:', contador, 'Multiplicador:', multiplicador);
  }, [contador, multiplicador]); // Especifique as dependências: 'contador' e 'multiplicador'

  return (
    <div>
      <p>Contador: {contador}</p>
      <p>Multiplicador: {multiplicador}</p>
      <button onClick={() => setContador(contador + 1)}>Incrementar Contador</button>
      <button onClick={() => setMultiplicador(multiplicador * 2)}>Dobrar Multiplicador</button>
    </div>
  );
};

export default ExemploComDependencias;

Aqui a gente tá colocando [count, multiplicador] como dependências no useEffect. Especificar dependências no useEffect pode melhorar a performance e eficiência do teu código, porque evita que o efeito rode sem necessidade.

Não Faça:

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

const ExemploDependenciaIncorreta = () => {
  const [contador, setContador] = useState(0);
  const [multiplicador, setMultiplicador] = useState(1);

  useEffect(() => {
    // Este efeito será executado após cada renderização
    console.log('Efeito executado! Contador:', contador, 'Multiplicador:', multiplicador);
  }); // Nenhuma dependência especificada

  return (
    <div>
      <p>Contador: {contador}</p>
      <p>Multiplicador: {multiplicador}</p>
      <button onClick={() => setContador(contador + 1)}>Incrementar Contador</button>
      <button onClick={() => setMultiplicador(multiplicador * 2)}>Dobrar Multiplicador</button>
    </div>
  );
};

export default ExemploDependenciaIncorreta;

Sempre coloque todas as dependências relevantes pra garantir que o efeito funcione como esperado.

Nesse caso, o useEffect não tem nenhuma dependência o useEffect então vai rodar o efeito depois de cada renderização, mesmo se nenhuma das dependências mudou. Isso pode causar problemas de desempenho, especialmente se o efeito for computacionalmente custoso.

  1. Faça a limpeza

O useEffect devolve uma função de limpeza. Ele chama essa função antes de rodar o efeito novamente ou quando o componente for desmontado. Se o efeito cria recursos, como timers ou inscrições, é bom limpar esses recursos usando a função de limpeza.

Faça:

import React, { useEffect } from 'react';

const ExemploAssinatura = () => {
  useEffect(() => {
    const assinatura = assinarAlgo();

    // Limpeza: Cancelar a assinatura quando o componente é desmontado
    return () => {
      cancelarAssinatura(assinatura);
    };
  }, []); // Array de dependências vazio

  return <p>Exemplo de Assinatura</p>;
};

const assinarAlgo = () => {
  // Simular a configuração da assinatura
  console.log('Assinado!');
  return 'token_de_assinatura';
};

const cancelarAssinatura = (assinatura) => {
  // Simular a limpeza da assinatura
  console.log('Assinatura cancelada!', assinatura);
};

export default ExemploAssinatura;

Pra arrumar a bagunça depois de uma inscrição, a gente cancela a inscrição quando o componente é desmontado. Isso garante que não vai ter inscrições penduradas causando problemas.

Não Faça:

import React, { useEffect } from 'react';

const ExemploLimpezaIncorreta = () => {
  useEffect(() => {
    const intervalo = setInterval(() => {
      console.log('Olá!');
    }, 1000);

    // Ops! Sem limpeza, isso causará vazamento de memória
  }, []); // Array de dependências vazio

  return <p>Exemplo de Limpeza Incorreta</p>;
};

export default ExemploLimpezaIncorreta;

Nesse caso, a gente esqueceu de limpar depois de criar um intervalo. Isso pode causar um vazamento de memória, porque o intervalo continua rodando mesmo depois do componente ser desmontado. Sempre lembre de limpar os recursos ou inscrições pra manter tua aplicação sem bugs.

  1. Use React Hooks para criar hooks personalizados

Isso significa que dá pra criar suas próprias ferramentas reutilizáveis usando React Hooks pra lidar com tarefas específicas nos teus componentes React. É tipo criar tuas próprias peças de LEGO personalizadas que se encaixam perfeitamente nos teus projetos, deixando o teu trabalho ainda mais fácil e organizado.

Por exemplo, se tu precisa sempre gerenciar a autenticação do usuário, dá pra construir um hook personalizado usando React Hooks que cuida do login, logout e verifica se o usuário está logado. Assim, não precisa ficar recriando o mesmo código em todo componente – tu só usa teu hook personalizado, como uma ferramenta especial na tua caixa de ferramentas de React, quando precisar. É uma maneira bem legal e eficiente de tornar teus projetos React mais manejáveis.

import React, { useState } from 'react';

// Crie um hook personalizado para gerenciar o estado de autenticação
const useAutenticacao = () => {
  const [logado, setLogado] = useState(false);

  // Função para lidar com o login
  const fazerLogin = () => {
    setLogado(true);
  };

  // Função para lidar com o logout
  const fazerLogout = () => {
    setLogado(false);
  };

  // Retorna o estado de autenticação e funções para login e logout
  return {
    logado,
    fazerLogin,
    fazerLogout
  };
};

// Um componente que utiliza o hook de autenticação personalizado
const ComponenteAutenticacao = () => {
  // Use o hook personalizado para obter o estado de autenticação e funções
  const { logado, fazerLogin, fazerLogout } = useAutenticacao();

  return (
    <div>
      <p>O usuário está logado: {logado ? 'Sim' : 'Não'}</p>
      <button onClick={fazerLogin}>Logar</button>
      <button onClick={fazerLogout}>Sair</button>
    </div>
  );
};

export default ComponenteAutenticacao;

Pra gerenciar o status de autenticação nesse exemplo, a gente criou um novo hook chamado useAutenticacao. Esse hook único é usado pelo ComponenteAutenticacao pra gerenciar o processo de login e logout.

import React, { useState } from 'react';

// Este componente está lidando incorretamente com a autenticação sem usar um hook personalizado

const ComponenteAutenticacaoInvalido = () => {
  // Estado para controlar se o usuário está logado ou não
  const [logado, setLogado] = useState(false);

  // Função para lidar com o login
  const fazerLogin = () => {
    setLogado(true); // Define logado como true quando o botão 'Entrar' é clicado
  };

  // Função para lidar com o logout
  const fazerLogout = () => {
    setLogado(false); // Define logado como false quando o botão 'Sair' é clicado
  };

  return (
    <div>
      {/* Exibe se o usuário está logado ou não */}
      <p>O usuário está logado: {logado ? 'Sim' : 'Não'}</p>
      <button onClick={fazerLogin}>Entrar</button> {/* Botão para iniciar login */}
      <button onClick={fazerLogout}>Sair</button> {/* Botão para iniciar logout */}
    </div>
  );
};

export default ComponenteAutenticacaoInvalido; // Exporta o componente para uso em outras partes da aplicação

Em vez de colocar a lógica num hook personalizado, a gente tá usando o useState diretamente dentro do componente nesse exemplo. Além de ir contra as práticas recomendadas, que sugerem encapsular a lógica relacionada dentro de um hook personalizado pra reutilização, isso pode resultar em duplicação de código. Criar essas ferramentas especiais (hooks personalizados) é o caminho certo.

Conclusão

Os React Hooks, como o useState e o useEffect, são ferramentas poderosas que podem tornar seu código mais modular, reutilizável, limpo e fácil de manter.

Use o useState para gerenciar o estado essencial do componente, garantindo imutabilidade e normalização. Use o useEffect para lidar com efeitos colaterais, como busca de dados e inscrições.

Seguindo esses princípios, você pode alcançar um novo nível de eficiência e elegância na sua jornada de desenvolvimento com React.

Bons hooks! 🚀

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

0 Comments