React Hooks: Não é mágica, apenas arrays

Com diagramas para explicar as regras dessa nova abordagem

Image for post
Image for post
Não existe mágica, são apenas arrays! — Créditos imagem

Eu sou um grande fã da nova API Hooks. No entanto, tem algumas restrições estranhas sobre como você deva usá-la. Para aqueles que estão lutando para entender as razões dessas novas regras, vou apresentar um modelo de como pensar ao utilizar essa nova API.

Entendendo como Hooks funcionam

As regras dos Hooks

  • Não chame Hooks dentro de loops, condições ou funções
  • Utilize Hooks apenas de dentro de funções React

Este último eu acho que é evidente. Para anexar o comportamento a um componente funcional, você precisa ser capaz de associar esse comportamento ao componente de alguma forma.

O primeiro, no entanto, acho que pode ser confuso, pois pode parecer pouco natural programar usando uma API como essa e é isso que eu quero explorar hoje.

Arrays, arrays e arrays! Gerenciamento de estado em Hooks é tudo sobre arrays!

Por favor, note que isso é especulação e apenas um exemplo de implementação da API para mostrar como você pode pensar sobre isso. Isso não é necessariamente como a API funciona internamente. Além disso, esta é apenas uma proposta. Tudo isso pode mudar no futuro.

Como poderíamos implementar useState()?

Primeiro vamos começar com um componente:

function RenderFunctionComponent() {
const [firstName, setFirstName] = useState("Rudi");
const [lastName, setLastName] = useState("Yardley");
return (
<Button onClick={() => setFirstName("Fred")}>Fred</Button>
);
}

A idéia por trás da API Hooks é que você pode usar uma função setter retornada como o segundo item do array da função do Hooks e que o setter controlará o estado que é gerenciado internamente.

Então, o que o React fará com isso?

1) Inicialização

Inicialização: Duas matrizes vazias, Cursor é 0
Inicialização: Duas matrizes vazias, Cursor é 0

2) Primeira renderização

Cada chamada ao useState(), quando executada pela primeira vez, envia uma função setter (ligada a uma posição do cursor) no array setters e, em seguida, envia algum estado para o array state.

Primeira renderização: Itens gravados nos arrays como incrementos do cursor.
Primeira renderização: Itens gravados nos arrays como incrementos do cursor.

3) Renderização subseqüente

Renderização subseqüente: itens lidos do array como incrementos de cursor
Renderização subseqüente: itens lidos do array como incrementos de cursor

4) Manipulação de eventos

Os setters “lembram” o seu índice e ajustam a memória de acordo com ele.
Os setters “lembram” o seu índice e ajustam a memória de acordo com ele.

E nossa implementação ingênua…

let state = [];
let setters = [];
let firstRun = true;
let cursor = 0;
function createSetter(cursor) {
return function setterWithCursor(newVal) {
state[cursor] = newVal;
};
}
// Pseudo-código para uma função `useState`
export function useState(initVal) {
if (firstRun) {
state.push(initVal);
setters.push(createSetter(cursor));
firstRun = false;
}
const setter = setters[cursor];
const value = state[cursor];
cursor++;
return [value, setter];
}
// Um exemplo de componente usando Hooks
function RenderFunctionComponent() {
const [firstName, setFirstName] = useState("Rudi"); // cursor: 0
const [lastName, setLastName] = useState("Yardley"); // cursor: 1
return (
<div>
<Button onClick={() => setFirstName("Richard")}>Richard</Button>
<Button onClick={() => setFirstName("Fred")}>Fred</Button>
</div>
);
}
// Uma espécie de simulação do ciclo de renderização do React
function MyComponent() {
cursor = 0; // redefinindo o cursor
return <RenderFunctionComponent />; // render
}
console.log(state); // Pré-renderização: []
MyComponent();
console.log(state); // Primeira renderização: ['Rudi', 'Yardley']
MyComponent();
console.log(state); // Renderizações subsequentes: ['Rudi', 'Yardley']
// clique no botão 'Fred'console.log(state); // Estado após o evento `click`: ['Fred', 'Yardley']

Por que a ordem é importante?

Vamos fazer o que a equipe do React diz que você não deve fazer:

let firstRender = true;function RenderFunctionComponent() {
let initName;

if(firstRender){
[initName] = useState("Rudi");
firstRender = false;
}
const [firstName, setFirstName] = useState(initName);
const [lastName, setLastName] = useState("Yardley");
return (
<Button onClick={() => setFirstName("Fred")}>Fred</Button>
);
}

Aqui nós temos uma chamada para useState em uma condicional. Vamos ver o caos que isso cria no sistema.

Primeira renderização ruim para o componente

Renderizando um Hook extra 'ruim' que será eliminado na próxima renderização
Renderizando um Hook extra ‘ruim’ que será eliminado na próxima renderização

Neste ponto, nossas instâncias de firstName e lastName contém os dados corretos, mas vamos dar uma olhada no que acontece no segundo render:

Renderizações subsequentes são piores

Ao remover o gancho entre os processos de renderização, obtemos um erro.
Ao remover o hook entre os processos de renderização, obtemos um erro.

Agora ambos firstName e lastName estão definidos como “Rudi” no nosso armazenamento de estado, tornando-se inconsistente. Isso é claramente errôneo e não funciona, mas nos dá uma idéia de por que as regras para Hooks são apresentadas do jeito que são.

A equipe do React está estipulando as regras de uso porque resultará em dados inconsistentes ao utilizar Hooks!

Pense em Hooks como uma manipulação de um conjunto de arrays, e você não quebrará as regras!

Portanto, o truque é pensar que Hooks estão gerenciando sua aplicação como um conjunto de arrays que precisam de um cursor consistente. Se você fizer isso tudo deve funcionar.

Conclusão

Hooks é uma API de plug-in efetiva para componentes do React. Existe uma razão pela qual as pessoas estão entusiasmadas com isso e se você pensar sobre esse tipo de modelo, onde o estado existe como um conjunto de arrays, então você não deve encontrar muitos problemas, bastar seguir as regras em torno de seu uso.

Espero dar uma olhada no método useEffects no futuro e tentar compará-lo aos métodos de ciclo de vida de componentes do React.

Você pode seguir Rudi Yardley no Twitter como @rudiyardley ou no Github como @ryardley

⭐️ Créditos

Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store