Explorando o Ciclo de Vida e Hooks do React

O React, uma biblioteca JavaScript popular para construir interfaces de usuário, evoluiu significativamente ao longo do tempo. Compreender seu ciclo de vida e a introdução dos Hooks é fundamental para desenvolver aplicações eficientes e modernas.

Evolução do React e Conceitos Fundamentais

Antes da versão 16.8.0, o desenvolvimento em React era predominantemente baseado em componentes de classe. A introdução dos Hooks nessa versão marcou uma mudança significativa, permitindo o uso de estado e outros recursos do React em componentes funcionais. As versões 17 e 18 consolidaram essa abordagem, com a versão 18 focando em componentes funcionais e Hooks, e abandonando o suporte a navegadores mais antigos como o Internet Explorer 11. Para garantir compatibilidade em ambientes que carecem de recursos modernos de navegador, pode-se considerar a inclusão de um polyfill global.

Polyfills e Shims

Um Polyfill é um código que implementa funcionalidades nativas que não são suportadas pelo ambiente de execução atual. Um Shim, por outro lado, é uma biblioteca que introduz uma nova API em um ambiente legado, utilizando apenas os recursos existentes nesse ambiente.

Ciclo de Vida dos Componentes de Classe

O ciclo de vida de um componente de classe no React pode ser dividido em três fases principais:

Fase de Inicialização (Pré-React 14)

Iniciada pela chamada de ReactDOM.render():

  • constructor(): Inicializa o estado do componente e vincula métodos.
  • componentWillMount() (Obsoleto): Executado antes da montagem inicial.
  • render(): Retorna a estrutura UI do componente. Deve ser uma função pura.
  • componentDidMount(): Executado após o componente ser montado no DOM. Ideal para chamadas de API e manipulação do DOM.

Fase de Atualização (Pré-React 14)

Iniciada por this.setState() dentro do componente ou por uma nova renderização do componente pai:

  • componentWillReceiveProps() (Obsoleto): Chamado antes de um componente montado receber novas props.
  • shouldComponentUpdate(): Determina se o componente deve ser re-renderizado. Permite otimização de performance.
  • componentWillUpdate() (Obsoleto): Executado antes da atualização visual ocorrer.
  • render(): Executado novamente para refletir as mudanças.
  • componentDidUpdate(): Executado após a atualização visual do componente. Útil para reagir a mudanças em props ou estado.

Fase de Desmontagem (Pré-React 14)

Iniciada por ReactDOM.unmountComponentAtNode():

  • componentWillUnmount(): Executado antes do componente ser desmontado. Essencial para limpeza (cancelar timers, remover listeners, etc.).

Ciclo de Vida em Versões Posteriores (React 14+)

A partir do React 14, o ciclo de vida foi atualizado, com a introdução de novos métodos e a depreciação de outros.

Fase de Inicialização

  • constructor()
  • getDerivedStateFromProps(nextProps, prevState): Método estático que permite atualizar o estado interno com base nas props recebidas. Substitui componentWillReceiveProps.
  • render()
  • componentDidMount()

Fase de Atualização

  • getDerivedStateFromProps(nextProps, prevState)
  • shouldComponentUpdate(nextProps, nextState)
  • render()
  • getSnapshotBeforeUpdate(prevProps, prevState): Executado logo antes das alterações serem refletidas no DOM. Permite capturar informações do DOM (como posição de scroll) antes da atualização.
  • componentDidUpdate(prevProps, prevState, snapshot): Recebe o valor retornado por getSnapshotBeforeUpdate como terceiro argumento (snapshot).

Fase de Desmontagem

  • componentWillUnmount()

Hooks: A Nova Era dos Componentes Funcionais

Hooks permitem usar estado e outros recursos do React em componentes funcionais, tornando o código mais conciso e legível.

useState

Permite adicionar estado a componentes funcionais. Retorna um par: o valor do estado atual e uma função para atualizá-lo.

{`
import { useState } from 'react';

function Contador() {
 const [contador, setContador] = useState(0);

 return (
   <div>
     <p>Você clicou {contador} vezes</p>
     <button onclick="{()"> setContador(contador + 1)}>
       Clique aqui
     </button>
   </div>
 );
}
   `}

useEffect

Permite executar efeitos colaterais em componentes funcionais. É equivalente a combinar componentDidMount, componentDidUpdate e componentWillUnmount.

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

function ExemploEfeito() {
 const [dados, setDados] = useState(null);

 // Executa após a montagem e sempre que 'idUsuario' mudar
 useEffect(() => {
   fetchDadosUsuario(idUsuario).then(dados => setDados(dados));
   // Função de limpeza, executada antes da desmontagem ou re-execução do efeito
   return () => {
     // Limpar recursos, como cancelamento de requisições
   };
 }, [idUsuario]); // Array de dependências

 // Executa apenas uma vez após a montagem (sem dependências)
 useEffect(() => {
   console.log("Componente montado!");
   const subscription = pubsub.subscribe('evento', minhaFuncao);
   return () => {
     pubsub.unsubscribe(subscription); // Limpeza
   };
 }, []);

 return <div>{dados ? dados.nome : 'Carregando...'}</div>;
}
   `}

useRef

Retorna um objeto mutável cuja propriedade .current é inicializada com o valor passado. É útil para acessar referências de elementos DOM ou para persistir valores mutáveis entre renderizações sem causar re-renderizações.

{`
import React, { useState, useRef } from 'react';

function Timer() {
 const [tempo, setTempo] = useState(0);
 const intervalRef = useRef(null);

 const iniciarTimer = () => {
   intervalRef.current = setInterval(() => {
     setTempo(prevTempo => prevTempo + 1);
   }, 1000);
 };

 const pararTimer = () => {
   clearInterval(intervalRef.current);
 };

 // Exemplo de acesso ao DOM
 const inputRef = useRef(null);
 const focusInput = () => {
   inputRef.current.focus();
 };

 return (
   <div>
     <p>Tempo: {tempo}s</p>
     <button onclick="{iniciarTimer}">Iniciar</button>
     <button onclick="{pararTimer}">Parar</button>
     <input ref="{inputRef}" type="text"></input>
     <button onclick="{focusInput}">Focar Input</button>
   </div>
 );
}
   `}

useContext

Permite consumir um valor de um React Context. Facilita o compartilhamento de dados entre componentes sem a necessidade de passar props manualmente em todos os níveis.

useCallback e useMemo

São Hooks de otimização de performance.

  • useCallback: Retorna uma versão memorizada da callback, útil para evitar re-renderizações desnecessárias de componentes filhos que dependem dessa callback.
  • useMemo: Retorna um valor memorizado. Útil para otimizar cálculos custosos, recalculando o valor apenas quando uma de suas dependências muda.
{`
import React, { useState, useCallback, useMemo } from 'react';

function OtimizadoComponente({ items }) {
 const [filtro, setFiltro] = useState('');

 // useMemo para calcular o resultado de forma otimizada
 const itensFiltrados = useMemo(() => {
   console.log('Calculando itens filtrados...');
   return items.filter(item => item.includes(filtro));
 }, [items, filtro]);

 // useCallback para memorizar a função de callback
 const handleFiltroChange = useCallback((event) => {
   setFiltro(event.target.value);
 }, []); // Sem dependências, a função nunca muda

 return (
   <div>
     <input onchange="{handleFiltroChange}" placeholder="Filtrar..." type="text" value="{filtro}"></input>
     <ul>
       {itensFiltrados.map((item, index) => (
         <li key="{index}">{item}</li>
       ))}
     </ul>
   </div>
 );
}
   `}

Gerenciamento de Estado e Ferramentas

Para aplicações maiores, o gerenciamento de estado se torna crucial. Ferramentas como Redux, Redux Saga, Dva e Umi oferecem soluções robustas.

Redux

Segue o padrão Flux com um único store centralizado, actions e reducers para gerenciar o estado de forma previsível e com fluxo de dados unidirecional.

Redux Saga

Um middleware para Redux que facilita o gerenciamento de efeitos colaterais complexos e assíncronos, utilizando Generators.

Dva

Um framework de gerenciamento de estado baseado em Redux, Redux Saga e Umi Router, que simplifica a estrutura do projeto com o conceito de Models.

UmiJS

Um framwork de desenvolvimento de aplicações React, conhecido por sua extensibilidade e convenções, que integra ferramentas como Dva e Ant Design.

Comunicação com o Servidor (Requisições HTTP)

A comunicação entre o cliente e o servidor é essencial. Diversas abordagens existem:

XMLHttpRequest (XHR)

A API nativa do JavaScript para realizar requisições HTTP. Foi a base para o desenvolvimento do Ajax.

jQuery Ajax

Uma abstração sobre o XHR que simplifica a realização de requisições Ajax, embora dependa da biblioteca jQuery.

Axios

Uma biblioteca popular baseada em Promises para realizar requisições HTTP. Oferece recursos como interceptores, cancelamento de requisições e tratamento de erros.

{`
import axios from 'axios';

async function buscarDados() {
 try {
   const response = await axios.get('/api/dados', {
     params: { id: 123 },
     headers: { 'Authorization': 'Bearer token' }
   });
   console.log(response.data);
 } catch (error) {
   console.error('Erro ao buscar dados:', error);
 }
}
   `}

Fetch API

A API moderna e baseada em Promises para realizar requisições de rede. É mais flexível que o XHR, mas requer tratamento adicional para certos cenários (como rejeição de status de erro e timeouts).

{`
async function enviarDados() {
 try {
   const response = await fetch('/api/enviar', {
     method: 'POST',
     headers: {
       'Content-Type': 'application/json',
     },
     body: JSON.stringify({ nome: 'Exemplo', valor: 100 }),
   });

   if (!response.ok) {
     throw new Error(`Erro HTTP: ${response.status}`);
   }
   const resultado = await response.json();
   console.log(resultado);
 } catch (error) {
   console.error('Erro ao enviar dados:', error);
 }
}
   `}

Estratégias para Lidar com Cross-Origin Requests (CORS)

A política de mesma origem (Same-Origin Policy) restringe requisições entre domínios diferentes. Soluções comuns incluem:

  • JSONP: Uma técnica legada que utiliza tags <script> para obter dados de outro domínio. Suporta apenas requisições GET.
  • CORS (Cross-Origin Resource Sharing): Um padrão W3C que permite aos servidores especificar quais origens têm permissão para acessar seus recursos. A configuração é feita no lado do servidor.

Ao configurar o servidor (por exemplo, com Express), você pode adicionar cabeçalhos apropriados:

{`
app.use((req, res, next) => {
 res.setHeader('Access-Control-Allow-Origin', '*'); // Ou um domínio específico
 res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
 res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
 next();
});
   `}

Estrutura de um Projeto React com UmiJS

A criação e configuração de um projeto UmiJS envolve:

  1. Instalação do Node.js.
  2. Criação do projeto usando o CLI do Umi: npx @umijs/create-umi-app nome-do-projeto.
  3. Instalação das dependências: npm install.
  4. Execução do projeto: npm run start.
  5. Configuração de rotas no arquivo .umirc.ts ou em um arquivo dedicado (router.tsx). Umi suporta rotas configuradas e rotas por convenção (baseadas na estrutura de arquivos).

A navegação entre rotas pode ser feita usando o hook history do Umi.

{`
import { history } from 'umi';

// Navegar para uma rota
history.push('/pagina-destino');

// Navegar com parâmetros de query
history.push({
 pathname: '/pagina-destino',
 query: { id: '123', nome: 'Teste' },
});
   `}

Modelos (.tsx dentro de src/models) são usados para gerenciar estado com Dva, definindo state, reducers (para atualizações síncronas) e effects (para lógica assíncrona).

A conexão entre componentes e modelos é feita com a função connect, similar ao React-Redux, permitindo acessar o estado e despachar actions.

Tags: React Ciclo de Vida Hooks useState useEffect

Publicado em 7-10 21:51