Como criar um hook customizado

Como criar um hook customizado

Todo programador busca escrever um código que seja reutilizável da melhor maneira. Sobre o desenvolvimento web, mais especificamente em React, os Hooks nos oferecem a opção de alterar estados e ciclo de vida a partir de componentes funcionais.

Neste artigo, vamos falar um pouco sobre o que são hooks e como criar um personalizado ao seu projeto.

Hooks

O que são?

Implementados na versão 16.8.0, os Hooks permitiram utilizar diversos recursos por meio de funções de uma forma simples, deixando de lado o uso de classes para acessar funcionalidades do React.

Os Hooks permitem reutilizar uma mesma funcionalidade diversas vezes na aplicação. Desta forma, fornecem uma maneira mais simples e concisa de lidar com o estado e o ciclo de vida em componentes funcionais, tornando o código mais legível e mais fácil de manter.

Hooks mais utilizados

useState

É usado para adicionar estado a componentes funcionais, ou seja, permite adicionar uma variável de estado ao seu componente. Ele retorna um par de valores: o estado atual e uma função para atualizá-lo. Exemplo:

import { useState } from 'react';

export default function Main(){
const [state, setState] = useState(0);

return (
  <div>
  <p>{state}</p>
  <button onClick={() => setState(state + 1)}>+</button>
  </div>
);
}

useEffect

O useEffect permite executar efeitos colaterais no código. Ele é usado para lidar com tarefas assíncronas, integração com APIs externas, manipulação de eventos, entre outros. Exemplo:

import { useState, useEffect } from 'react';

export default function Main(){
const [state, setState] = useState(0);

useEffect(() => {
  document.title = `Você clicou ${count} vezes`;
});

return (
  <div>
  <p>Você clicou {count} vezes</p>
  <button onClick={() => setCount(count + 1)}>
    Clique aqui
  </button>
  </div>
);
};

Neste exemplo, o título do documento tornou-se uma mensagem customizada, informando o número de cliques do botão.

useRef

Esse hook permite criar uma referência mutável que persiste entre renderizações. Pode ser usado para acessar o DOM diretamente ou para armazenar um valor imutável durante o ciclo de vida do componente. No exemplo abaixo, criamos a referência ao elemento da DOM utilizando o useRef. Esta referência é utilizada para focar o elemento quando o botão for clicado.

import { useRef } from 'react';

export default function Home() {
const element = useRef(null);

const handleButton = () => {
  element.current.focus();
};

return (
  <div className='container'>
  <form>
    <input type="text" ref={element} />
    <button onClick={handleButton}>Clicar</button>
  </form>
  </div>
);
};

useContext

Esse hook permite que você acesse o contexto de um componente, com o objetivo de compartilhar dados globalmente, sem precisar passar props manualmente através de cada nível. Veja o exemplo abaixo:

import { createContext, useContext, useState } from 'react';

const CountContext = createContext();

function CountProvider({ children }) {
  const [count, setCount] = useState(0);

  return (
    <CountContext.Provider value={{ count, setCount }}>
      {children}
    </CountContext.Provider>
  );
}

export { CountProvider, CountContext };

import { useContext } from 'react';
import { CountContext } from './CountContext';

function handleCount() {
  const { count } = useContext(CountContext);

  return <p>Contador: {count}</p>;
}

function handleButton() {
  const { count, setCount } = useContext(CountContext);

  const increment = () => {
    setCount(prevCount => prevCount + 1);
  };

  return <button onClick={increment}>Incrementar</button>;
}

import { CountProvider } from './CountContext';
import handleCount from './handleCount';
import handleButton from './handleButton';

export default function App() {
  return (
    <CountProvider>
      <div>
        <handleCount />
        <handleButton />
      </div>
    </CountProvider>
  );
};

Primeiramente, cria-se o contexto por meio da função createContext. Ele terá o estado que será compartilhado entre componentes. Em seguida, o contexto é importado nos componentes que precisam acessar o estado global. Por fim, na função App, os componentes (handleCount e handleButton) são envolvidos pelo provedor criado para compartilhar o estado global.

O estado count pode ser compartilhado entre os componentes handleCount e handleButton, evitando a necessidade de passar props manualmente entre os componentes pais e filhos.

useCallback

Utilizado para evitar a criação de novas funções em cada renderização, especialmente quando essas funções são passadas como props para componentes filhos, otimizando o desempenho.

import { useState, useCallback } from 'react';

export default function FilteredList() {
  const [items, setItems] = useState([
    'Maçã', 'Banana', 'Laranja', 'Morango', 'Uva'
  ]);

  const [filterText, setFilterText] = useState('');

  const handleFilterChange = useCallback((event) => {
    setFilterText(event.target.value);
  }, []);

  const filteredItems = items.filter(item =>
    item.toLowerCase().includes(filterText.toLowerCase())
  );

  return (
    <div>
      <input
        type="text"
        placeholder="Filtrar itens..."
        value={filterText}
        onChange={handleFilterChange}
      />
      <ul>
        {filteredItems.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
};

Neste exemplo, o useCallback é usado para memorizar a função handleFilterChange, que é responsável por atualizar o estado filterText com base no texto digitado pelo usuário. Sem o useCallback, a função handleFilterChange seria recriada a cada renderização do componente FilteredList, o que poderia levar a re-renderizações desnecessárias do componente.

Por que hook customizado?

O uso de hooks customizados no React oferece diversas vantagens para a aplicação. A reutilização de lógica é um dos principais pontos, pois a criação de hooks personalizados permite encapsular uma lógica complexa em um único lugar. Isso facilita a reutilização dessa lógica em vários componentes, evitando a duplicação de código.

Quando encapsulamos detalhes complexos de implementação nos hooks customizados, conseguimos deixar uma interface mais simples e clara para os componentes que os utilizam.

Além disso, quando for preciso modificar o código do hook, somente um único local precisará de alteração. Isso facilita a manutenção e reduz a chance de introduzir erros ao atualizar várias partes do código.

Vamos à prática!

  1. Configurações iniciais do projeto

Vamos iniciar o projeto do zero. Crie uma pasta onde ele será armazenado. Aqui iremos nomear como custom-hook. Em seu terminal, rode o seguinte código:

npx create-react-app custom-hook

Em seguida:

cd custom-hook

npm start

Uma nova janela irá se abrir em seu navegador, como mostrado na figura abaixo.


Agora, iremos fazer a organização das nossas pastas e arquivos. Neste ponto, você deve estar com a seguinte estrutura de pastas:

Podemos deletar os seguintes arquivos:

  • Na pasta public:
  • logo192.png e logo512.png;
  • manifest.json;
  • robots.txt.
  • Na pasta src iremos apagar todos, exceto o index.js e App.js;

Mais alguns ajustes precisam ser feitos:

  • Na pasta public, em index.html: retire os comentários e referências das imagens que apagamos.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta name="theme-color" content="#000000" />
  <meta
  name="description"
  content="Web site created using create-react-app"
  />
  <title>React App</title>
</head>
<body>
  <noscript>You need to enable JavaScript to run this app.</noscript>
  <div id="root"></div>
</body>
</html>

  • Na pasta src:
  • Crie uma nova pasta chamada pages;
  • Na pasta pages, adicione um novo arquivo Home.js com o seguinte trecho:

export default function Home() {
return (
  <div>
      <h1>Home</h1>
  </div>
);
};

  • Crie mais dois arquivos na pasta pages, denominados About.js e Contact.js. Eles terão o seguinte conteúdo:

About.js

export default function About() {
return (
  <div>
  <h1>About</h1>
  </div>
);
};

Contac.js

export default function Contact() {
return (
  <div>
  <h1>Contact</h1>
  </div>
);
};

  • No arquivo App.js, que está em src, deixe somente o seguinte código:

export default function App() {
return (
  <div>
  <h1>Custom Hooks</h1>
  </div>
);
};

  • E por fim, modifique o arquivo index.js:

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
  <App />
</React.StrictMode>
);

Com isso, não teremos mais nenhum erro no terminal:

Suas pastas e arquivos estão com esta estrutura agora:

E sua página estará com esta cara inicial:

2. Roteamento

Precisaremos instalar o react-router-dom, que será a biblioteca responsável pelo roteamento das nossas páginas. No terminal, rode:

npm i react-router-dom

Com o react-router-dom instalado, vamos importá-lo no nosso App.js, juntamente com nossas pages. As importações ficarão assim:

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { About } from './pages/About';
import { Contact } from './pages/Contact';
import { Home } from './pages/Home';

O BrowserRouter é o responsável pelo roteamento, ficando por volta das rotas do projeto. Por convenção, ele é renomeado como Router. O Route são as rotas propriamente ditas, e o Routes é quem descreve as rotas, ficando por volta delas.

Agora, iremos adicionar as rotas de cada página:

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import About from './pages/About';
import Contact from './pages/Contact';
import Home from './pages/Home';

export default function App() {
return (
  <Router>
  <Routes>
    <Route path='/' element={<Home />} />
    <Route path='/about' element={<About />} />
    <Route path='/contact' element={<Contact />} />
  </Routes>
  </Router>
);
};

O Route apresenta dois parâmetros: o path e o element. O path é a localização que o usuários irá utilizar para acessar aquela página. No element, terá o componente que será renderizado para aquela rota, no caso serão a Home, About e Contact.

3. Desenvolvendo o Hook

Dentro de src, crie uma pasta chamada hooks. Dentro desta nova pasta, vamos criar um arquivo chamado usePath.js, no qual iremos desenvolver nosso hook customizado.

A ideia deste hook é verificar em qual página o usuário se encontra e obter o path dela. Para obter este parâmetro, vamos utilizar useLocation, que é um hook nativo do React. O usePath ficará da seguinte forma:

import { useLocation } from "react-router";

export default function usePath() {
    let isHome = false;
    const { pathname } = useLocation();

    if (pathname === '/') {
        isHome = true;
    };

    return {
        isHome
    };
};

Iremos desestruturar o useLocation para utilizar somente o pathname. Utilizaremos a variável isHome como uma flag. Caso o usuário esteja na página Home, a variável isHome será true. Do contrário, ela será false.

Assim, basta importar o nosso hook nas nossas páginas e elaborar a lógica. O código em cada página ficará:

Home.js

import usePath from "../hooks/usePath";

export default function Home() {
const {isHome} = usePath();

return (
  <div>
  <h1>Home</h1>
  {isHome ?
    <h3 style={{color:'green'}}>Você está na página principal</h3> :
    <h3 style={{color:'red'}}>Você não está na página principal</h3>
    }
  </div>
);
};

About.js

import usePath from "../hooks/usePath";

export default function About() {
const {isHome} = usePath();

return (
  <div>
  <h1>About</h1>
  {isHome ?
    <h3 style={{color:'green'}}>Você está na página principal</h3> :
    <h3 style={{color:'red'}}>Você não está na página principal</h3>
  }
  </div>
);
};

Contact.js

import usePath from "../hooks/usePath";

export default function Contact() {
const {isHome} = usePath();

return (
  <div>
  <h1>Contact</h1>
  {isHome ?
    <h3 style={{color:'green'}}>Você está na página principal</h3> :
    <h3 style={{color:'red'}}>Você não está na página principal</h3>
  }
  </div>
);
};

Importamos o hook usePath em cada page e desestruturamos para ter acesso a variável isHome. Adicionamos um ternário para a lógica de verificação da rota. Caso esteja na home a frase em verde ‘Você está na página principal’ irá aparecer. Se não, a frase em vermelho ‘Você não está na página principal’ será renderizada na tela.

Nossa resultado final:

Conclusão

Neste artigo, abordamos sobre os hooks, que são ferramentas muito utilizadas no React. Vimos também alguns hooks nativos do React e como desenvolver um hook customizado. Criar um hook personalizado à sua aplicação oferece várias vantagens, dentre elas, tem-se a melhora na organização, reutilização e legibilidade do seu código.

💡
As opiniões e comentários expressos neste artigo são de propriedade exclusiva de seu autor e não representam necessariamente o ponto de vista da Revelo.

A Revelo Content Network acolhe todas as raças, etnias, nacionalidades, credos, gêneros, orientações, pontos de vista e ideologias, desde que promovam diversidade, equidade, inclusão e crescimento na carreira dos profissionais de tecnologia.