DEV Community

Cover image for Os melhores custom hooks do react
Yuri Sampaio
Yuri Sampaio

Posted on

Os melhores custom hooks do react

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
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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;
};
Enter fullscreen mode Exit fullscreen mode

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' }}
    />
  )
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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];
}
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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!

Referencias

https://usehooks-ts.com

Top comments (0)