Os melhores custom hooks do react
Tags: javascript, react, webdev, beginners
Published: 2023-05-16 15:42:06

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

{% embed https://www.youtube.com/watch?v=TNhaISOUy6Q&t=478s %}

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 

```tsx
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

```tsx
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

```tsx
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

```tsx
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


```tsx
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

```tsx
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](https://www.phived.com)

```tsx
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

```tsx
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

```tsx
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

```tsx
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!

### Referencias

https://usehooks-ts.com