Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/Iniciando com React/8.7-useCallback.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function ProductPage({ productId, referrer, theme }) {

Neste exemplo, o componente **ProductPage** é uma pagina na qual o usuário pode fazer a compra de um determinado produto, através do formulário que é disponibilizado, caso não houvesse um useCallback, toda vez que alterasse o tema da aplicação, o componente seria re-renderizado e junto dele o _handleSubmit_. Com o useCallback é possível ver que ao mudar o tema da aplicação a função não sofrerá mudanças e consequentemente não precisará ser `armazenada` novamente.

[Ir para a próxima seção](./9-React%20Error%20Boundaries.md)
[Ir para a próxima seção](./8.8-custom-hooks.md)

<p align="center">
<a href="https://github.com/he4rt/4noobs" target="_blank">
Expand Down
138 changes: 138 additions & 0 deletions docs/Iniciando com React/8.8-custom-hooks.md

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(block): Ajuste no local do arquivo

Precisamos mover esse arquivo pra essa pasta: https://github.com/he4rt/react4noobs/tree/master/docs/Iniciando%20com%20React

E renomear ele pra 8.8-custom-hooks

Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<p align="center">
<a href="https://github.com/he4rt/4noobs" target="_blank" title="Clique para visualizar mais informações sobre o projeto 4noobs">
<img src="../../assets/global/header-4noobs.svg" alt="Cabeçalho do repositório representado pelo logotipo da He4rt, simbolizado por um coração roxo, na esquerda e a tipografia '4 noobs by He4rt devs' na direita">
</a>
</p>

# **CUSTOM HOOKS: Compartilhando lógica entre componentes**

## O que é um Custom Hook?

Hook personalizado é uma função JavaScript na qual é possível reutilizar
sua lógica em diversos componentes da aplicação. Imagine que na sua
aplicação você tem vários componentes que precisam fazer requisições a
uma API, ou salvar algo no local storage. Se você tiver um hook
personalizado que faça tais coisas, não será necessário repetir o mesmo
código várias vezes, apenas chamar o hook.

Um hook personalizado não é um recurso separado do React, é apenas uma
função comum que usa, por baixo dos panos, hooks nativas como **`useState`**
e **`useEffect`**.

Hooks personalizados não compartilham estados entre os componentes: cada
componente que utiliza o hook possui seu estado isolado, a função
compartilha apenas a lógica. A função precisa, obrigatoriamente, começar
com a palavra **`use`**, essa convenção avisa o React que aquela função pode
chamar outras hooks internamente, permitindo que ferramentas como o
ESLint verifiquem se as regras das hooks estão sendo seguidas corretamente.

## Vantagens dos Custom Hooks:

- **Reutilização de lógica:** Evita duplicar o mesmo código (fetch de API, manipulação de formulário, localStorage, etc.) em vários componentes diferentes.

- **Componentes mais limpos:** A lógica complexa fica isolada na hook, deixando o componente focado apenas na renderização.

- **Facilidade de manutenção:** Se precisar corrigir um bug ou mudar uma regra de negócio, você altera em um único lugar (a hook), em vez de em cada componente que repete a lógica.

- **Testabilidade:** Como a lógica fica separada da interface, fica mais fácil testar a hook isoladamente, sem precisar renderizar todo o componente.

- **Não quebra a árvore de componentes:** Diferente de HOCs e render props, custom hooks não adicionam camadas extras de componentes na árvore, evitando o "wrapper hell".

- **Compartilhamento sem acoplamento:** Vários componentes podem usar a mesma lógica sem precisar estar relacionados entre si (não é necessário passar props entre pai e filho).

## Exemplo Prático: useFetch

Este hook encapsula toda a lógica de requisição a uma API, controlando
os estados de carregamento, erro e dados retornados.

### O código:

```javascript
import { useState, useEffect } from 'react';

export function useFetch(url){

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(suggestion): Adicionar cleanup no useEffect pra evitar race condition

Como o useEffect dispara a cada mudança de url, requisições podem resolver fora de ordem, uma resposta antiga chega depois da nova e sobrescreve o data correto. Como é um tutorial, vale mostrar o padrão de cleanup, que é o jeito idiomático de lidar com isso:

useEffect(() => {
    if (!url) {
        setLoading(false);
        return;
    }

    let ignore = false; // flag: ignora resultado se o effect já foi limpo

    const fetchData = async () => {
        setLoading(true);
        setError(null);

        try {
            const respon
            }
            const json = await response.json();
            if (!ignore) setData(json);
        } catch (err) {
            if (!ignore) {
                setError(err.message);
                setData(null);
            }
        } finally {
            if (!ignore) setLoading(false);
        }
    };

    fetchData();

    // Cleanup: roda quando a url muda ou o componente desmonta
    return () => {
        ignore = true;
    };
}, [url]);

O let ignore = false + return () => { ignore = true } garante que só a requisição mais recente atualiza o estado.

Alternativa mais robusta seria AbortController pra cancelar o fetch de verdade, mas o ignore já resolve o problema visível e é mais simples de explicar para um iniciante.

const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);

useEffect( () => {
if (!url) {
setLoading(false);
return;
}

let ignore = false; // flag: ignora resultado se o effect já foi limpo

//Função que busca a API
const fetchData = async () => {
setLoading(true); //Ativa o Loading quando começa a busca
setError(null); // Reseta erros anteriores

try{
const response = await fetch(url);
const json = await response.json();

if (!ignore) setData(json);
}catch (err){
if (!ignore) {
setError(err.message);
setData(null);
}
}finally{
if (!ignore) setLoading(false);
}
};

fetchData();

// Cleanup: roda quando a url muda ou o componente desmonta.
return () => {
ignore = true;
};
}, [url]); //UseEffect roda toda vez que a variável url muda.

//Retorna os estados em forma de objetos, dessa maneira é possível pegar o qual variável quiser.
return { data, loading, error };
}
```

### Como funciona?

- Recebe uma **`url`** como parâmetro.
- Retorna um objeto com três valores: **`data`** (os dados retornados),
**`loading`** (se a requisição está em andamento) e **`error`** (mensagem de
erro, se houver).
- O **`useEffect`** roda automaticamente sempre que a **`url`** mudar, disparando
uma nova requisição.

### Usando a hook em um componente:

```jsx
import {useFetch} from 'caminho_da_funcao'

function ListaPokemon() {
const { data, loading, error } = useFetch('https://pokeapi.co/api/v2/pokemon');

if (loading) return <p>Carregando...</p>;
if (error) return <p>Erro: {error}</p>;

return (
<ul>
{data.results.map((pokemon) => (
<li key={pokemon.name}>{pokemon.name}</li>
))}
</ul>
);
}
```
Perceba como o componente ficou mais limpo: Ele não sabe **como** a requisição é feita, apenas **usa** o resultado.

[Ir para a próxima seção](./9-React%20Error%20Boundaries.md)

<p align="center">Made with 💜</p>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(block): Antes disso, precisamos colocar o link para a próxima seção, tipo como é feito aqui: https://github.com/he4rt/react4noobs/blob/master/docs/Iniciando%20com%20React/8.3-useContext.md


<p align="center">
<a href="https://github.com/he4rt/4noobs" target="_blank">
<img src="../../assets/global/footer-4noobs.svg" width="380" alt="Tipografia com o título '4 noobs by He4rt devs' e o slogan 'Da comunidade para a comunidade 💜'">
</a>
</p>