Explorando os Hooks Adicionais do React: Exemplos e Casos de Uso
Tags: javascript, react, reactnative, hooks
Published: 2023-05-23 17:25:26

## Introdução:

O React é uma biblioteca JavaScript amplamente utilizada para a construção de interfaces de usuário interativas. Desde a introdução dos Hooks no React 16.8, os desenvolvedores têm desfrutado de uma maneira mais simples e eficiente de compartilhar lógica entre componentes funcionais. 

Além dos Hooks básicos, como **useState**, **useEffect** e **useContext**, o React também oferece Hooks adicionais que fornecem funcionalidades extras. 

Neste artigo, exploraremos esses Hooks adicionais, discutiremos seus casos de uso e será fornecido exemplos práticos.

### useReducer:

O Hook **useReducer** é uma alternativa ao useState quando se lida com estados mais complexos que envolvem múltiplas transições. Ele aceita um **reducer** e um estado inicial, retornando o estado atual e uma função **dispatch** para disparar ações. 

É útil quando se trabalha com lógica de estado mais avançada, como gerenciamento de formulários ou estados de aplicativos complexos.

**Exemplo:**

```js
import React, { useReducer } from 'react';

const initialState = { count: 0 };

const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
};

const Counter = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </div>
  );
};

```


### useRef:

O Hook **useRef** retorna um objeto mutável que persiste durante todo o ciclo de vida do componente. É útil para acessar e manter uma referência a um elemento do DOM, armazenar valores mutáveis entre renderizações ou para armazenar qualquer valor que precise ser persistido durante o ciclo de vida do componente.

**Exemplo:**

```js
import React, { useRef } from 'react';

const InputWithFocus = () => {
  const inputRef = useRef(null);

  const handleClick = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={handleClick}>Focus Input</button>
    </div>
  );
};

```


### useMemo:

O Hook **useMemo** é usado para memorizar o resultado de uma função de cálculo pesado e evitar recalculá-la em cada renderização. 

Ele recebe uma função de cálculo e um array de dependências e retorna o valor memorizado. É útil quando o cálculo é computacionalmente intensivo e os valores de entrada não mudam com frequência.

**Exemplo:** 

```js
import React, { useMemo } from 'react';

const ExpensiveCalculation = () => {
  const result = useMemo(() => {
    // Cálculo pesado
    return performExpensiveCalculation();
  }, [dependencies]);

  return <div>Result: {result}</div>;
};

```


### useCallback:

O hook **useCallback** é usado para memorizar uma função e evitar a criação desnecessária de uma nova instância dessa função a cada renderização do componente. 

Ele é útil quando você deseja otimizar o desempenho de componentes que dependem de funções que não precisam ser recriadas sempre que o componente é renderizado. 

Vamos explorar abaixo alguns casos de uso e fornecer um exemplo prático.

#### Casos de Uso:

1. Passar uma função para um componente filho: Quando um componente pai passa uma função para um componente filho como prop, o uso do **useCallback** pode evitar que a função seja recriada a cada renderização do componente pai, garantindo que o componente filho só seja atualizado quando realmente necessário.
2. Otimizar a criação de manipuladores de eventos: Em componentes que possuem eventos que disparam ações complexas ou fazem cálculos intensivos, o **useCallback** pode ser usado para garantir que o manipulador de eventos seja criado apenas uma vez, evitando renderizações desnecessárias.

**Exemplo:** 

```js
import React, { useCallback, useState, memo } from 'react';

const ChildComponent = memo(({ handleClick }) => {
  // Componente filho que é renderizado quando o handleClick é atualizado
  return <button onClick={handleClick}>Click Me</button>;
});

const ParentComponent = () => {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    setCount((prevCount) => prevCount + 1);
  }, []);

  return (
    <div>
      <p>Count: {count}</p>
      <ChildComponent handleClick={handleClick} />
    </div>
  );
};

```


No exemplo acima, o hook **useCallback** é usado para memorizar a função **handleClick** no componente pai. Dessa forma, mesmo que o componente pai seja renderizado várias vezes, a função **handleClick** não será recriada, garantindo que o componente filho seja renderizado apenas quando o estado **count** for atualizado.

### useImperativeHandle:

O hook **useImperativeHandle** é usado para expor funções e propriedades específicas de um componente funcional para componentes pai. 

Ele permite que você controle a instância do componente filho e exponha métodos personalizados para serem acessados externamente. 

Vamos explorar abaixo alguns casos de uso e fornecer um exemplo prático.

#### Casos de Uso:

1. Integração com bibliotecas de terceiros: Em alguns casos, você pode precisar integrar seu componente funcional com uma biblioteca externa que requer o acesso direto a alguns métodos ou propriedades do componente. O **useImperativeHandle** permite que você exponha apenas o que é necessário, mantendo o controle sobre a interface externa do componente.
2. Componentes customizados com APIs específicas: Se você está criando um componente personalizado que precisa ser controlado externamente, como um componente de formulário que precisa ser validado ou enviado, o **useImperativeHandle** pode ser usado para expor métodos relevantes para o controle externo.


**Exemplo:**

```js
import React, { useRef, useImperativeHandle, forwardRef } from 'react';

// Componente filho que deseja expor um método
const ChildComponent = forwardRef((props, ref) => {
  const inputRef = useRef(null);

  useImperativeHandle(ref, () => ({
    focusInput: () => {
      inputRef.current.focus();
    },
  }));

  return <input ref={inputRef} type="text" />;
});

// Componente pai que acessa o método exposto pelo componente filho
const ParentComponent = () => {
  const childRef = useRef(null);

  const handleClick = () => {
    childRef.current.focusInput();
  };

  return (
    <div>
      <ChildComponent ref={childRef} />
      <button onClick={handleClick}>Focus Input</button>
    </div>
  );
};

```


No exemplo acima, o hook **useImperativeHandle** é usado no componente filho para expor o método **focusInput**. O componente filho utiliza o hook **useRef** para criar uma referência ao elemento de input e, em seguida, o hook **useImperativeHandle** é usado para expor o método **focusInput** através da referência passada como segundo argumento.

No componente pai, um botão é renderizado e, quando clicado, chama o método **focusInput** exposto pelo componente filho através da referência **childRef**. Dessa forma, é possível acessar o elemento de input no componente filho e aplicar o foco diretamente.

### useLayoutEffect:

O hook **useLayoutEffect** é bastante semelhante ao **useEffect**, porém é sincronizado com o ciclo de renderização do React, o que significa que ele é executado imediatamente após as mudanças no DOM serem aplicadas, mas antes do browser concluir a pintura da tela. 

Ele é útil em casos em que você precisa realizar tarefas de manipulação de layout que dependem das medidas ou posições dos elementos na tela. 

Vamos explorar abaixo alguns casos de uso e fornecer um exemplo prático.

#### Casos de Uso:

1. Manipulação de medidas e posições de elementos: O **useLayoutEffect** pode ser usado quando você precisa obter as medidas ou posições de elementos no DOM que foram atualizadas após a renderização do componente. Por exemplo, se você precisa calcular a altura ou a largura de um elemento ou precisa posicionar um elemento com base em outras medidas.
2. Atualização de animações ou efeitos visuais: Quando você deseja criar animações ou efeitos visuais que dependem de informações atualizadas de layout, como transições suaves ou animações que envolvem mudanças na posição ou tamanho dos elementos, o **useLayoutEffect** pode ser usado para garantir que essas atualizações sejam aplicadas antes da pintura da tela.
3. Interações com bibliotecas externas: Se você está integrando seu componente com uma biblioteca externa que espera que o DOM esteja atualizado antes de realizar determinadas ações, o **useLayoutEffect** pode ser útil para garantir que as mudanças no DOM sejam aplicadas antes da interação com a biblioteca.

**Exemplo:**

```js
import React, { useLayoutEffect, useRef } from 'react';

const ComponentWithLayoutEffect = () => {
  const elementRef = useRef(null);

  useLayoutEffect(() => {
    // Obtém as medidas do elemento após a atualização do DOM
    const { width, height } = elementRef.current.getBoundingClientRect();
    
    // Atualiza algum estado ou realiza alguma ação com base nas medidas obtidas
    console.log(`Largura: ${width}px, Altura: ${height}px`);
  });

  return <div ref={elementRef}>Exemplo de Componente com useLayoutEffect</div>;
};

```

No exemplo acima, o hook useLayoutEffect é usado para realizar uma ação após a atualização do DOM. O elemento `<div>` é referenciado usando o hook **useRef**, permitindo o acesso ao elemento em questão.

Dentro do **useLayoutEffect**, as medidas do elemento são obtidas usando o método **getBoundingClientRect()**, que retorna um objeto contendo informações sobre as dimensões e a posição do elemento no viewport. 

Neste exemplo, estamos apenas exibindo as medidas no console, mas você pode realizar qualquer ação necessária com base nessas medidas.

É importante ressaltar que o **useLayoutEffect** é síncrono e bloqueante, o que significa que ele pode causar um atraso na renderização do componente. Portanto, é importante usá-lo com cuidado e considerar se o **useEffect** seria mais adequado em determinadas situações.

### useDebugValue:

O hook **useDebugValue** é um hook de diagnóstico do React que permite fornecer um rótulo personalizado para um valor customizado, facilitando a identificação e depuração de componentes durante o desenvolvimento. 

Ele não tem impacto na renderização ou funcionalidade do componente, mas fornece informações úteis ao utilizar ferramentas de desenvolvimento.

#### Casos de Uso:

1. Exibição de informações personalizadas: O **useDebugValue** pode ser usado para exibir informações personalizadas sobre um valor durante a depuração. Isso é especialmente útil quando você possui um valor complexo ou derivado que deseja inspecionar facilmente enquanto depura seu componente.
2. Identificação de componentes em ferramentas de desenvolvimento: Ao usar ferramentas de desenvolvimento que suportam o hook **useDebugValue** , você pode fornecer um rótulo significativo para um componente, tornando mais fácil identificar e distinguir componentes específicos na árvore de componentes.
3. Visualização de estado interno: Se você tiver algum estado interno no componente que não é diretamente visível no componente renderizado, o **useDebugValue** pode ser usado para expor esse estado durante a depuração, permitindo uma melhor compreensão do estado interno do componente.

**Exemplo:**

```js
import React, { useDebugValue, useState } from 'react';

const ComponentWithDebugValue = () => {
  const [count, setCount] = useState(0);

  useDebugValue(`Count: ${count}`);

  const increment = () => {
    setCount(prevCount => prevCount + 1);
  };

  return (
    <div>
      <button onClick={increment}>Increment</button>
      <p>Count: {count}</p>
    </div>
  );
};

```

No exemplo acima, o hook **useDebugValue** é usado para fornecer um rótulo personalizado para o valor count. Isso significa que, ao inspecionar o componente em ferramentas de desenvolvimento, o rótulo **"Count: {valor}"** será exibido, facilitando a identificação do estado do contador durante a depuração.

Ao clicar no botão "Increment", o estado count é atualizado e o componente é renderizado novamente. Durante a renderização, o hook **useDebugValue** garante que o rótulo personalizado seja atualizado, permitindo que você acompanhe facilmente o valor atualizado do contador durante a depuração.

Embora o exemplo acima seja simples, o **useDebugValue** é particularmente útil em cenários mais complexos, onde você pode ter valores personalizados ou derivados que precisam ser monitorados e inspecionados durante o desenvolvimento.

### useDeferredValue:

O hook **useDeferredValue** é um hook introduzido no React 18 que permite atrasar a atualização de um valor de estado durante a renderização. Ele é útil para otimizar o desempenho de componentes que possuem valores de estado que não precisam ser atualizados imediatamente.

#### Casos de Uso:

1. Atualizações não críticas: Se você tiver um valor de estado que é atualizado com frequência, mas as atualizações não são críticas para a renderização imediata do componente, você pode usar o **useDeferredValue** para atrasar a atualização desse valor. Isso pode melhorar o desempenho, reduzindo a frequência de atualizações e renderizações desnecessárias.
2. Animações suaves: O **useDeferredValue** é especialmente útil para criar animações suaves em componentes. Você pode atrasar a atualização do valor de estado usado para controlar a animação, garantindo que a animação seja executada de forma mais fluida e sem interrupções.
3. Carregamento de dados assíncronos: Se você estiver buscando dados assíncronos e deseja evitar atualizações rápidas do estado enquanto os dados estão sendo buscados, o **useDeferredValue** pode ser usado para atrasar a atualização do estado até que os dados sejam completamente carregados, evitando renderizações desnecessárias.

**Exemplo:**

```js
import React, { useState, useDeferredValue } from 'react';

const ComponentWithDeferredValue = () => {
  const [count, setCount] = useState(0);

  const deferredCount = useDeferredValue(count, { timeoutMs: 1000 });

  const increment = () => {
    setCount(prevCount => prevCount + 1);
  };

  return (
    <div>
      <button onClick={increment}>Increment</button>
      <p>Count: {deferredCount}</p>
    </div>
  );
};

```

No exemplo acima, o hook **useDeferredValue** é usado para atrasar a atualização do valor de estado **count** por 1 segundo.
Durante esse período, se o estado **count** for atualizado várias vezes, apenas a última atualização será refletida na renderização do componente.

O valor atrasado **deferredCount** é utilizado na renderização do componente, garantindo que apenas a atualização mais recente seja refletida no elemento `<p>`. Isso evita renderizações desnecessárias e melhora o desempenho do componente.

No caso de múltiplas atualizações do estado **count** dentro do período de atraso, apenas a atualização mais recente será visível após o término do período de atraso.

O **useDeferredValue** é especialmente útil quando você possui atualizações rápidas e frequentes em um valor de estado, mas deseja otimizar o desempenho do componente, evitando renderizações desnecessárias causadas por atualizações não críticas.

### useTransition:

O hook **useTransition** é um hook introduzido no React 18 que permite adicionar transições suaves a alterações de estado. 

Ele é útil para criar animações e efeitos visuais fluidos durante as transições de renderização.

#### Casos de Uso:

1. Atualizações intensivas: Quando você tem atualizações de estado intensivas que podem causar interrupções visuais no seu aplicativo, o **useTransition** pode ser usado para suavizar essas transições. Ele permite que você aguarde um período curto antes de aplicar a atualização no estado, proporcionando uma experiência mais agradável aos usuários.
2. Transições de carregamento: Se o seu aplicativo exibe elementos de carregamento ou indicadores de progresso, o **useTransition** pode ser usado para criar uma animação suave durante a transição entre o estado de carregamento e o estado concluído. Isso ajuda a evitar mudanças abruptas na interface do usuário e mantém os usuários engajados durante o processo de carregamento.
3. Animações de entrada e saída: O **useTransition** é útil para criar animações de entrada e saída em elementos ou componentes. Por exemplo, você pode usar o hook para aplicar um efeito de desvanecimento ao exibir ou ocultar um modal, ou para animar a aparência de um elemento na tela quando um evento ocorre.

**Exemplo:**

```js
import React, { useState, useTransition } from 'react';

const ComponentWithTransition = () => {
  const [showContent, setShowContent] = useState(false);
  const [startTransition, isPending] = useTransition({
    timeoutMs: 500, // Duration of the transition
  });

  const handleClick = () => {
    startTransition(() => {
      setShowContent(!showContent);
    });
  };

  return (
    <div>
      <button onClick={handleClick}>
        {showContent ? 'Hide Content' : 'Show Content'}
      </button>
      {isPending ? (
        <p>Loading...</p>
      ) : (
        showContent && <p>Content to be shown or hidden</p>
      )}
    </div>
  );
};

export default ComponentWithTransition;

```

Quando o botão é clicado, a função **handleClick** é acionada. Dentro dela, chamamos **startTransition** e passamos uma função de retorno que atualiza o estado **showContent**. 

Essa função de retorno será envolvida por uma transição, tornando a atualização de estado mais suave visualmente.

No render do componente, renderizamos condicionalmente o conteúdo com base em **showContent**. Se a transição estiver pendente (durante o período de transição), exibimos uma mensagem de carregamento. Caso contrário, mostramos o conteúdo apenas se **showContent** for true.

O hook **useTransition** nos permite adicionar um efeito de transição às atualizações de estado, proporcionando uma experiência visual mais suave para o usuário. 

É especialmente útil ao lidar com mudanças intensas de estado ou ao adicionar animações durante as transições de IU.

Observação: O hook useTransition requer a versão 18 ou superior do React

### useId:

O hook **useId** é usado para gerar um identificador único dentro de um componente no React. Ele é útil em casos em que você precisa atribuir um **ID** a um elemento de forma dinâmica, garantindo que o **ID** seja exclusivo em várias instâncias desse componente.

O caso de uso mais comum para o **useId** é quando você está renderizando uma lista de elementos e precisa atribuir um **ID** único a cada item da lista. 

Dessa forma, cada item terá um identificador exclusivo, facilitando a referência e manipulação desses elementos.

**Exemplo:**

```js
import React from 'react';
import { useId } from 'react-id-generator';

const ListaItens = ({ itens }) => {
  return (
    <ul>
      {itens.map((item) => (
        <Item key={useId()} texto={item} />
      ))}
    </ul>
  );
};

const Item = ({ texto }) => {
  const itemId = useId(); // Gera um ID único para cada item

  return <li id={itemId}>{texto}</li>;
};

const App = () => {
  const itens = ['Item 1', 'Item 2', 'Item 3'];

  return <ListaItens itens={itens} />;
};

export default App;

```

Nesse exemplo, temos um componente **ListaItens** que recebe uma array de itens. Em seguida, iteramos sobre cada item usando o método map e, para cada item, usamos o **useId()** como o valor da propriedade key. Isso garante que cada item na lista tenha um **ID** exclusivo.

Dentro do componente **Item**, usamos novamente o **useId()** para gerar um **ID** único para cada item individualmente. Em seguida, atribuímos esse **ID** ao elemento `<li>` através da propriedade **id**.

Assim, cada item na lista terá um **ID** exclusivo gerado pelo **useId()**, permitindo identificar e manipular facilmente esses elementos de forma independente.

O hook **useId** é útil em situações em que você precisa gerar **IDs** únicos dinamicamente, evitando conflitos de **ID** em componentes renderizados repetidamente.


## Conclusão:

Os Hooks adicionais do React fornecem aos desenvolvedores ainda mais flexibilidade e poder para criar componentes funcionais eficientes e reutilizáveis

## Referencias
[React Hooks](https://legacy.reactjs.org/docs/hooks-reference.html#usesyncexternalstore)
[React.dev](https://react.dev/)