Quando pensamos em React a primeira coisa que vem em mente é hooks, o quanto eles podem nos ajudar a implementar features e resolver problemas todo mundo já sabe, portanto o intuito do artigo é compartilhar alguns custom hooks que foram úteis no desenvolvimento das minhas aplicações e podem te ajudar a resolver alguns problemas pontuais, evitando a criação de complexidade desnecessária.
Mas antes de irmos para os custom hooks em si, caso não esteja totalmente confortável com os hooks do react ainda sugiro esse video para relembrar rapidamente o funcionamento de cada
Sem mais delongas, vamos para o que interessa!!
Custom Hooks
1 useMediaQuery
Provavelmente você já se deparou com uma situação onde era necessário que comportamentos diferentes existissem dependendo do tamanho de tela, por exemplo uma navbar acima de 768px e abaixo desse breakpoint um botão para renderizar uma sidebar colapsável, vamos aos detalhes no hook
import { useEffect, useState } from 'react'
function useMediaQuery(query: string): boolean {
const getMatches = (query: string): boolean => {
// Previne problemas com SSR
if (typeof window !== 'undefined') {
return window.matchMedia(query).matches
}
return false
}
//State para verificar se passou ou não do breakpoint
const [matches, setMatches] = useState<boolean>(getMatches(query))
function handleChange() {
setMatches(getMatches(query))
}
useEffect(() => {
const matchMedia = window.matchMedia(query)
handleChange()
if (matchMedia.addListener) {
matchMedia.addListener(handleChange)
} else {
matchMedia.addEventListener('change', handleChange)
}
return () => {
if (matchMedia.removeListener) {
matchMedia.removeListener(handleChange)
} else {
matchMedia.removeEventListener('change', handleChange)
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [query])
return matches
}
export default useMediaQuery
Como usar
import { useMediaQuery } from 'usehooks-ts'
export default function Component() {
const matches = useMediaQuery('(min-width: 768px)')
return (
<div>
{`A viewport é ${matches ? 'no mínimo' : 'menor que'} 768 pixels wide`}
</div>
)
}
2 useDebounce
Para os que ainda não estão familiarizados com o pattern Debounce, consiste em uma estratégia para diminuir as requisições feitas a partir de um input, sendo amplamente utilizados em um campo de busca por exemplo, evitando que a gente sobrecarregue o nosso back-end ou em caso de api publica que exceda a quantidade de chamadas por dia, vamos aos detalhes no hook
import { useEffect, useState } from 'react'
//O value é valor que está mudando e chamando a api
function useDebounce<T>(value: T, delay?: number): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value)
useEffect(() => {
// Esse delay é o delay entre as chamadas para a api
const timer = setTimeout(() => setDebouncedValue(value), delay || 500)
return () => {
clearTimeout(timer)
}
}, [value, delay])
return debouncedValue
}
export default useDebounce
Esse Custom hook é o meu favorito pela simplicidade de implementar um pattern e seus benefícios em uma aplicação.
Como usar
import { ChangeEvent, useEffect, useState } from 'react'
import { useDebounce } from 'usehooks-ts'
export default function Component() {
const [value, setValue] = useState<string>('')
const debouncedValue = useDebounce<string>(value, 500)
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value)
}
useEffect(() => {
// Faz o seu fetch ou a logica aqui
// Ativa quando o "debouncedValue" mudar
}, [debouncedValue])
return (
<div>
<p>Valor em tempo real: {value}</p>
<p>Valor do debounce: {debouncedValue}</p>
<input type="text" value={value} onChange={handleChange} />
</div>
)
}
3 useClickOutside
A chance de você fazer um modal ao lidar com uma aplicação front-end é muito grande, algumas aplicações tem vários modais diferentes, e em todos eles a gente gasta um tempo considerável para deixar o mais detalhado, acessível e performático possível.
Com esse hook conseguimos implementar a feature de fechar o modal ao clicar fora dele de forma simples e rápida, vamos a implementação dele
import { useEffect, useRef } from 'react';
export const useClickOutside = (SetStateHandler: () => void) => {
const domNode = useRef<HTMLDivElement>(null);
useEffect(() => {
let handler = (event: MouseEvent) => {
if (!domNode.current?.contains(event.target as Node)) {
SetStateHandler();
}
};
document.addEventListener('mousedown', handler);
return () => {
document.removeEventListener('mousedown', handler);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return domNode;
};
Como usar
import { useRef } from 'react'
import { useOnClickOutside } from 'usehooks-ts'
export default function Component() {
const ref = useRef(null)
const handleClickOutside = () => {
// Sua logica customizada aqui
console.log('clicked outside')
}
const handleClickInside = () => {
// Sua logica customizada aqui
console.log('clicked inside')
}
useOnClickOutside(ref, handleClickOutside)
return (
<button
ref={ref}
onClick={handleClickInside}
style={{ width: 200, height: 200, background: 'cyan' }}
/>
)
}
4 useLocalStorage
Em muitas aplicações você precisa guardar informações como tema, idioma e outras preferencias do usuário no localStorage, para isso você acaba criando uma lógica complicada para verificar se o elemento existe ou não no localStorage, portanto esse hook foi desenvolvido com o intuito de simplificar a integração com essa api do browser
Um projeto famoso que usa esse hook para lidar com informação do usuário é o Phived Phived
import { useState } from 'react';
export function useLocalStorage<T>(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useState<T>(() => {
if (typeof window === 'undefined') {
return initialValue;
}
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.log(error);
return initialValue;
}
});
const setValue = (value: T | ((val: T) => T)) => {
try {
const valueToStore =
value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
if (typeof window !== 'undefined') {
window.localStorage.setItem(key, JSON.stringify(valueToStore));
}
} catch (error) {
console.log(error);
}
};
return [storedValue, setValue] as const;
}
Como usar
import { useLocalStorage } from 'usehooks-ts'
export default function Component() {
const [isDarkTheme, setDarkTheme] = useLocalStorage('darkTheme', true)
const toggleTheme = () => {
setDarkTheme((prevValue: boolean) => !prevValue)
}
return (
<button onClick={toggleTheme}>
{`The current theme is ${isDarkTheme ? `dark` : `light`}`}
</button>
)
}
5 useHover
Já tentou fazer animações legais com hover? fazer alguma logica acontecer sempre que tiver com o mouse sobre um objeto? Para simplificar a implementação de coisas assim que esse hook existe
function useHover() {
const [value, setValue] = useState(false);
const ref = useRef(null);
const handleMouseOver = () => setValue(true);
const handleMouseOut = () => setValue(false);
useEffect(
() => {
const node = ref.current;
if (node) {
node.addEventListener("mouseover", handleMouseOver);
node.addEventListener("mouseout", handleMouseOut);
return () => {
node.removeEventListener("mouseover", handleMouseOver);
node.removeEventListener("mouseout", handleMouseOut);
};
}
},
[ref.current] // Chama de novo se o ref mudar
);
return [ref, value];
}
Como usar
import { useRef } from 'react'
import { useHover } from 'usehooks-ts'
export default function Component() {
const hoverRef = useRef(null)
const isHover = useHover(hoverRef)
return (
<div ref={hoverRef}>
{`The current div is ${isHover ? `hovered` : `unhovered`}`}
</div>
)
}
Conclusão
Existem uma infinidade de custom hooks no react resolvendo diversos problemas diferentes, entretanto, mais importante que conhecer muito deles é saber o momento certo de acoplar mais código ao seu projeto, tenha certeza da necessidade de usar alguma feature antes de usar um custom hook e use com moderação.
Obrigado por ler até aqui, espero que eu tenha lhe ajudado de alguma forma, se gostar deixa um like!
Top comments (0)