Criando Custom Hooks com ReactJS
Anteriormente nesta jornada, você viu o funcionamento de alguns hooks do próprio ReactJS, como:
- useState
- useEffect
- useContext
- E entre outros...
Hoje nós iremos aprender a criar nossos próprios hooks em nossas aplicações feitas com ReactJS 😄
E fique tranquilo, pois esse processo não é tãããão complicado assim 😋
O que são custom hooks?
Custom Hooks nada mais são do que hooks customizados que podemos criar dentro das nossas aplicações seguindo a nomenclatura use
.
Esses hooks são consideradas funções em javascript que permitem que você extraia a lógica de componentes do ReactJS, para que elas possam ser reutilizados em diferentes partes de sua aplicação.
Graças aos custom hooks, nós podemos ter uma lógica de implementação mais reutilizável, onde conseguimos fazer essa separação por meio de funções separadas, o que facilita a reutilização dessa lógica em vários componentes.
Esses hooks devem ser criados seguindo a convenção de nome camelCase, o que é ideal para que o próprio ReactJS identifique que o arquivo criado se trata de um hook personalizado, e que pode ser utilizado dentro de componentes em ReactJS ou outros hooks.
Vejamos agora alguns exemplos de nomes que podemos dar aos nossos custom hooks:
useFetch
useForm
useAuth
useMicilini
useFormularioContato
- E entre outros...
Perceba que todos eles contam com o termo use
no início e segue o padrão camelCase
.
Além disso, dentro de um custom hook, você pode utilizar hooks do ReactJS como useState
, useEffect
, useContext
, entre outros, da mesma forma que faria dentro de um componente em ReactJS.
Criando seu projeto de testes
Antes de começarmos, vamos criar um novo projeto em ReactJS dedicado a esta lição. No meu caso criei um novo projeto chamado de custom-hooks
:
npx create-react-app custom-hooks
Após isso, não se esqueça de fazer aquela limpeza do código, e também aquela organizada nas pastas do src (organização por funcionalidade) 😁
Criando o componente de Nomes
Para quesitos exemplares, começaremos criando um novo componente chamado de Nomes
, que será responsável por trazer uma lista de nomes de uma API externa, e mostrar na tela para o usuário.
Nomes > index.jsx
:
import React, { useState, useEffect } from 'react';
function Nomes() {
const [nomes, setNomes] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchNomes() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
if (!response.ok) {
throw new Error('Erro ao buscar os nomes');
}
const data = await response.json();
setNomes(data);
setLoading(false);
} catch (error) {
console.error('Erro ao buscar nomes:', error);
setError(error); // Armazena o erro no estado
setLoading(false);
}
}
fetchNomes();
}, []);
return (
<div>
{loading && <p>Carregando...</p>}
{error && <p>Ocorreu um erro: {error.message}</p>}
{!loading && !error && (
<div>
<h2>Lista de Nomes</h2>
<ul>
{nomes.map(nome => (
<li key={nome.id}>{nome.name}</li>
))}
</ul>
</div>
)}
</div>
);
}
export default Nomes;
E não se esqueça de chamar esse componente dentro do App.js
:
import Nomes from './components/Nomes';
export default function App() {
return (
<div>
<Nomes />
</div>
);
}
Veja como ficou o resultado final:
![](https://micilini.com/assets/img/conteudos/reactjs/contents/rjs-114.png)
Beleza, o código acima funciona perfeitamente, mas você sabia que você pode otimizá-lo ainda mais usando Custom Hooks?
Que tal criar um hook customizado chamado de useFetch
que vai separar a lógica que estamos fazendo dentro do nosso componente Nomes
?
Isso faria com que o código se tornasse mais modular e reutilizável em diferentes partes da sua aplicação.
Criando o hook useFetch
Para criar este hook, é recomendável que façamos primeiro uma organização na pasta do src, começando pela criação de uma nova pasta chamada de hooks.
![](https://micilini.com/assets/img/conteudos/reactjs/contents/rjs-115.png)
Que será o local onde nós iremos armazenar todos os custom hooks que formos criar daqui em diante 😉
Em seguida, vamos criar um novo arquivo chamado de useFetch.js
dentro daquela pasta que acabamos de criar, com seguinte conteúdo:
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Erro ao buscar os dados');
}
const jsonData = await response.json();
setData(jsonData);
setLoading(false);
} catch (error) {
console.error('Erro ao buscar dados:', error);
setError(error);
setLoading(false);
}
}
fetchData();
}, [url]);
return { data, loading, error };
}
export default useFetch;
Vamos analisar o código que criamos acima passo a passo.
useState
: é usado para criar estados data, loading e error que armazenam respectivamente os dados da API, o estado de carregamento e erros que possam ocorrer durante a requisição.
useEffect
: é usado para realizar a requisição HTTP
assim que o componente que utiliza o hook é montado (url é passada como dependência). Ele encapsula a lógica assíncrona para buscar dados da API.
fetchData
: é uma função assíncrona que realiza a requisição GET
para a URL
especificada (url). Se a requisição for bem-sucedida, os dados são convertidos para JSON
e armazenados em data. Em caso de erro, o erro é capturado e armazenado em error.
Retorno do Hook
: o hook retorna um objeto { data, loading, error }
, que pode ser desestruturado e utilizado pelo componente que o consome para renderizar conteúdo condicionalmente com base no estado de loading
e error
.
Em seguida podemos utilizar esse hook no nosso componente Nomes
da seguinte forma:
Nomes > index.jsx
:
import React from 'react';
import useFetch from '../../hooks/useFetch'; // Importa o hook useFetch
function Nomes() {
const { data: nomes, loading, error } = useFetch('https://jsonplaceholder.typicode.com/users');
return (
<div>
{loading ? (
<p>Carregando...</p>
) : error ? (
<p>Ocorreu um erro: {error.message}</p>
) : (
<div>
<h2>Lista de Nomes</h2>
<ul>
{nomes.map(nome => (
<li key={nome.id}>{nome.name}</li>
))}
</ul>
</div>
)}
</div>
);
}
export default Nomes;
Com isso temos nosso custom hook (useFetch
) implementado com sucesso, onde teremos o mesmo resultado de antes:
![](https://micilini.com/assets/img/conteudos/reactjs/contents/rjs-114.png)
Incrível não acha? E com isso podemos utilizar esse useFetch
em outras partes da aplicação, de modo a retornar não somente nomes, mas como outros dados que a nossa aplicação precisa.
Graças aos custom hooks nós temos os seguintes benefícios:
- Reutilização: evita repetição de código ao encapsular lógica complexa em hooks reutilizáveis.
- Legibilidade: melhora a legibilidade do código, separando preocupações de lógica de estado de apresentação.
- Manutenção: facilita a manutenção ao centralizar a lógica em um único lugar.
Arquivos da lição
Os arquivos dessa lição podem ser encontrados neste repositório do GitHub.
Conclusão
Os custom hooks são uma poderosa ferramenta em ReactJS para gerenciar o estado e comportamentos complexos de forma eficiente e organizada.
Até a próxima lição 🤩