# Custom Hooks - Tempo de leitura estimado: 9 minutos. ### O que são `hooks`? * Hooks são uma adição ao React (a partir da versão 16.8) onde se permite fazer uso do `state` e outras `features` do React sem a criação de `classes`. Mais informações na documentação do React. * Regras imprescindíveis para o uso de hooks: - *Only call Hooks at the top level. Don’t call Hooks inside loops, conditions, or nested functions.* - *Only call Hooks from React function components. Don’t call Hooks from regular JavaScript functions. (There is just one other valid place to call Hooks — your own custom Hooks. We’ll learn about them in a moment.)* * Exemplos de Custom Hooks: 1. [useIsMobile](https://codesandbox.io/s/useismobile-k8p9t) Esse custom hook aqui pode ser implementado de algumas formas. A conclusão que cheguei é fazer uso tanto da `innerWidth` da window quanto o `userAgent` do navegador, que em conjunto com alguma biblioteca de detecção de dispositivos, nos diga com exatidão onde estamos atuando em termos de responsividade. No exemplo abaixo o hook customizado recebe por parâmetro (análogo ao `constructor` de classe) duas propriedades, uma a largura da janela para ser testada e a segunda o `userAgent`. obs.: Com os parâmetros desta forma, se torna possível a utilização em sistemas SSR. ```js const isMobile = useIsMobile({ width: 768, customAgent: global.navigator.userAgent }); ``` A largura é diretamente testada contra a `window.innerWidth` e o `userAgent` para este caso, é testado com a biblioteca [ismobilejs](https://github.com/kaimallea/isMobile). O teste pode ser feito manualmente com `RegEx`, por exemplo, sem a necessidade de terceiros. O uso deste custom hook pode ser feito por demanda, isto é, por página/componente ou pode ser conectado no topo do seu App realizando um disparo à `store` para que todo o sistema fique consciente da mobilidade. - exemplo conectado à `store`: (obs, qualquer uso de estado causa um re-render, o que talvez não seja interessante para todo o App). ```js export const App = () => { const isMobile = useIsMobile({ width: 768, customAgent: global.navigator.userAgent }); const dispatch = useDispatch(); dispatch(setIsMobile(isMobile)); } ``` - exemplo utilizado por demanda: ```js function App() { const isMobile = useIsMobile({ width: 768, customAgent: global.navigator.userAgent }); return ( <div className="App"> <h1>{isMobile ? "This is on mobile" : "This is on desktop"}</h1> </div> ); } ``` Este custom hook faz uso de: 1. useState 2. useEffect 2. [useModalWrapper](https://codesandbox.io/s/usemodal-as-a-wrapper-uit30) Este custom hook faz uso da habilidade de [`Portals`](https://reactjs.org/docs/portals.html) do React onde se é possível montar um componente em qualquer outro lugar do DOM independente de contexto. Novamente, em analogia a um construtor, como parâmetro `bindToElement` é passado o nó desejado para a renderização. Este custom hook nos retorna as seguintes propriedades: 1. ModalWrapper, que nada mais nada menos é, um HOC. 2. isOpen, como o nome mesmo já diz, nos informa se a modal está aberta ou não. 3. openModal, atuando diretamente como controle para a abertura da modal. 4. closeModal, assim como a openModal porém para seu fechamento. Sua aplicação é bem direta, monte o Wrapper dentro de seu componente e coloque a modal desejada (isso pode variar de aplicação para aplicação e consequentemente sua estilização CSS também no quesito de posicionamento). O Wrapper recebe por `props` duas coisas: o `backdrop` e o `fade`, sendo o segundo apenas possível com o primeiro. O primeiro disponibiliza a habilidade de clicar fora da modal para seu fechamento (ou apertar ESC ou qualquer outra condicional) e o segundo cria uma janela translúcida por trás (puramente estilo). ```js const {ModalWrapper, isOpen, openModal, closeModal} = useModalWrapper({ bindToElement: global.document.getElementById('modal-root') }); { isOpen && ( <ModalWrapper backdrop={true} fade={true}> <MyBeautifulModal closeModal={closeModal} /> </ModalWrapper> ); } <button type="button" onClick={() => openModal()}> Click to Open </button> ``` Este custom hook faz uso de: 1. useState 2. useEffect 3. useRef 4. React e ReactDOM 3. [useIsLoading](https://codesandbox.io/s/useisloading-connected-9s8gt) Observação: idealmente utilizado com aplicações com gerenciamento de estado global como por exemplo o `redux`. Este custom hook eu diria até que chega a ser perfumaria no entanto achei interessante por duas razões, abstração de código assim como todos os demais hooks nos oferecem e independência do acesso à `store`. Fica bem simplificado seu uso. O componente que necessitar do Loading a partir de alguma ação assíncrona não precisará acessar a `store` de forma alguma. ```js const [isLoading, Loading] = useIsLoading(); if (isLoading) return <Loading />; return <MyComponentAfterLoading />; ``` Este custom hook faz uso de: 1. React 2. useSelector (react-redux) 3. Componente de Loading 4. useAuth Este custom hook pode ser implementado de diversas formas e isso pode variiar de acordo com a necessidade e implementação de cada projeto, no entanto a sua ideia é basicamente a mesma, prover mecanismos de autenticação para páginas. O custom hook useAuth pode retornar os métodos e propriedades abaixo diretamente ou pode-se dividir suas responsabilidades, conforme abaixo. ```js const {isLogged, doLogin, authInfo} = useLogin(); const {doLogout} = useLogout(); ``` As propriedades acima descritas abaixo: 1. isLogged, retorna verdadeiro ou falso para a condicional de autenticado ou não no sistema. 2. doLogin, dispara a ação para realizar a autenticação. 3. authInfo, retorna objeto com informações pertinentes à autenticação. 4. doLogout, dispara a ação pertinente para deslogar do sistema. Este custom hook faz uso de: 1. useEffect 2. useSelector e useDispatch (react-redux) 3. useHistory, useLocation (react-router-dom) 5. [useInput](http://codesandbox.io/s/useinput-v66nu) Este custom hook é também bem simples no entanto um tanto quanto útil quando se é preciso lidar com `inputs` de formulário. Bem direto, o custom hook retorna os valores dos inputs e o `handler` de `onChange`. A mágica do controle de estado está toda por dentro do `useInput`. ``` const [values, handleChange] = useInput(); function handleSubmit(e) { e.preventDefault(); console.log("values", values); } <form onSubmit={handleSubmit}> <input type="text" name="name" placeHolder="Name" onChange={handleChange} /> </form> ``` Este custom hook faz uso de: 1. useState Demais possibilidades: https://usehooks.com/