Uma das razões pelas quais podemos usar componentes funcionais como nosso componente principal é porque agora ele pode conter seu próprio “estado” usando Ganchos como useState . Por causa disso, é possível descartar todos os componentes baseados em classe.

Apesar desta vantagem dada pelos Hooks, ainda é possível cultivar práticas inadequadas ao usar useState em nossos componentes funcionais. Ainda não estamos seguros em potenciais armadilhas que podemos introduzir ao construir nossos componentes em forma de função.

Como você sabe se está usando useState errado? Continue lendo.

Estado mutante em vez de usar setState fornecido por useState

Em primeiro lugar, o estado de mutação é um grande problema no ecossistema React devido ao fato de que ele pratica intensamente o conceito de imutabilidade . Para demonstrar como você pode alterar o estado descuidadamente sem saber, considere o seguinte snippet de código:

 const [MyValue, setMyValue]=useState (0); MeuValor=55;

Isso é considerado a mutação de um estado diretamente. Estamos violando fortemente a regra prática ao manipular nosso estado de maneira adequada porque deveria ser tratado como imutável, a menos que chamemos o segundo elemento da matriz, setMyValue .

Como o valor do estado é “somente leitura”, você não pode modificá-lo dessa forma. Isso gerará um erro:

Outras maneiras de transformar um estado por engano incluem o seguinte exemplo:

 const [myValues, setMyValues]=useState ([1,2,3,4,5]); meusValores [2]=55;
const [myValues, setMyValues]=useState ([1,2,3,4,5]); //map cria uma nova matriz. Mas ainda está fazendo referência ao array antigo, portanto, neste caso, ainda estamos alterando o array myValues.
const newValues ​​=myValues.map ((item, idx)=> { if (idx===2) item=55; devolver item;
});

Neste exemplo, você está tentando modificar um valor de estado, que é uma matriz. Você pode modificá-lo, mas isso não causará uma”renderização”em seu componente, o que significa que o novo valor não será mostrado em sua IU.

Para mostrá-lo em tempo real, deixe-nos dar um exemplo prático de mutação uma matriz :

 deixe contar=0;
const App=()=> { const [stateVal, setStateVal]=React.useState ([1,2,3,4,5]); const onChangeArrayValues ​​=()=> { stateVal [count]="Alterado"; contagem +=1; alert ("array atualizado:"+ stateVal); } Retorna ( 

Alterando os valores do estado da matriz

Valores de matriz: {stateVal}

{/*

Resultado da soma: {multiplyByThree (5, 5)}

*/}
); }

Portanto, como podemos ver neste exemplo, embora tenhamos modificado a matriz de estado, ela não reflete nossa IU. React é inteligente o suficiente para saber se o estado está definido ou simplesmente”mutado”. Se houver uma mutação, não haverá problema de”renderização”em seus componentes para refletir o novo valor de estado em nossa IU.

O mesmo pode ser dito com o estado baseado em objeto:

 const App=()=> { const [stateVal, setStateVal]=useState ({val1:"Olá, mundo!"}); Retorna ( 

stateVal.val1="Valor mutado..."}> Estado de teste: {stateVal.val1}

) }

https://codepen.io/reciosonny/pen/ExNaagg

Podemos alterá-lo sem que o React perceba que você o alterou. O mesmo problema acontecerá como o último exemplo com a mutação de uma matriz: o novo valor não refletirá em nossa IU.

Neste exemplo, o estado ainda precisa ser definido corretamente usando a função setState fornecida por useState .

Isso não é exclusivo dos ganchos de estado. Na verdade, você pode cometer o mesmo erro de gerenciar estados em um componente baseado em classe.

Como você deve definir o estado?

Uma maneira de resolver isso é garantir que usamos uma abordagem imutável, como definir valores de estado usando um segundo elemento de useState , assim:

 const [myValues, setMyValues]=useState (0); setMyValues ​​(55);

Este é o método oficial de definir um valor de estado de maneira imutável. Usamos o segundo elemento, que é uma função para definir o estado.

Ainda podemos usar essa abordagem para estados baseados em objeto. No entanto, ainda devemos observar o conceito de imutabilidade ao modificar tal estado. Este snippet de código de amostra o ajudará a fazer o truque:

//Usando o método Object.assign:
const newState=Object.assign ({}, state, {[item.id]: item}); //Ou usando a sintaxe de propagação ES6:
const newState={... oldState, prop1:"valor modificado"};

Ao definir um estado para matrizes, a melhor maneira é recriar a matriz que você deseja modificar com suas alterações. Esta é uma das melhores maneiras que conheço para modificar a matriz:

 const [myValues, setMyValues]=useState ([1,2,3,4,5]); //Copiar novo conjunto de matrizes usando a sintaxe de propagação ES6
const newItems=[... meusValores];
newItems [2]=55;//modificando o elemento específico da matriz setMyValues ​​(newItems);//define a nova matriz com valores modificados

Esta é como seria em tempo real .

Neste snippet de código de amostra, estamos efetivamente garantindo que recriaremos essa matriz e, em seguida, aplicaremos as alterações no elemento específico que desejamos alterar. Com esse método, estamos informando ao React que um estado está sendo alterado de maneira imutável. Isso irá acionar o componente “renderizar novamente” sob o capô.

Passando useState em adereços de componentes filhos para usá-lo

Passar useState como adereços em outro componente é totalmente possível. Mas não há nenhum benefício em fazer isso porque você sempre pode chamar useState importando React na parte superior do seu código JavaScript e chamando-o em todos os seus componentes.

Este é o exemplo de snippet de código para demonstrar isso:

 import React, {Component, useState} de'react';
import {hot} de"react-hot-loader"; const NewComponent=({useStateFn})=> { const [val, setVal]=useStateFn (0);//usamos useState dos adereços passados ​​para este componente Retorna ( 

Valor: {val}



); } const App=()=> { Retorna (

Olá, mundo!

{/* Passamos useState no componente filho para que sejam consumidos */}
) }

Esta é uma prática ruim e você nunca deve usar useState dessa forma. Além disso, isso pode introduzir um código espaguete que pode tornar o aplicativo muito difícil de corrigir. Evite isso como uma praga.

Não colocar useState no topo do corpo do componente ou funções

De acordo com os documentos do React oficiais:

Não chame Hooks dentro de loops, condições ou funções aninhadas. Em vez disso, sempre use Ganchos no nível superior de sua função React

Como useState é um gancho, precisamos colocá-lo no nível superior de nosso componente, portanto, colocá-lo em áreas diferentes do nível superior pode potencialmente introduzir confusão dentro de nossa estrutura de componente.

Em vez de fazer isso:

 const App=()=> { const onValueChanged=(input)=> { setVal (entrada); } const [val, setVal]=useState (0); Retorna ( 

Olá, mundo!

) }

Faça isto:

 const App=()=> { const [val, setVal]=useState (0); const onValueChanged=(input)=> { setVal (entrada); } Retorna ( 

Olá, mundo!

) }

Usar essa prática recomendada nos permitirá evitar possíveis bugs relacionados à chamada de um estado quando nosso aplicativo ficar maior.

Usando useState em componentes de classe ou funções JavaScript regulares

Se você leu as regras de ganchos no React oficial docs, eles encorajam você a não colocar ganchos como useState na classe ou funções regulares de JavaScript. Isso ocorre porque os ganchos não funcionam muito bem com eles, especialmente em estruturas de componentes baseadas em classe.

Suponha que você ainda insista em usar useState em componentes baseados em classe, como neste exemplo:

 class App extends Component { render () { const [inputVal, setInputVal]=useState (""); Retorna ( 
setInputVal (e.target.value)}/>

Valor de entrada: {inputVal}

); } }

Isso é o que você verá:

Para este assunto, o React irá notificá-lo imediatamente de que é um caso de uso inválido de usar ganchos em um componente baseado em classe. Isso significa que não há como usar ganchos como useState nele.

Existem outros casos de uso sutis, mas estão usando a implementação incorreta de useState , como usá-lo em expressões de função simples. Aqui está um exemplo.

 const myFunction=(arg1, arg2, arg3)=> { const [myStateValue, setMyStateValue]=useState (""); //faça a lógica aqui...
}

Se você se lembra, as regras de ganchos dizem junto com essas linhas:

Não chame Hooks de funções JavaScript regulares

Então, este é um uso inválido de useState , a menos que usemos essa função como um gancho personalizado . Um gancho personalizado também é apenas uma função JavaScript, só que, desta vez, tem um ciclo de vida próprio, como adicionar useEffect para controlar as mudanças de seu estado.

Portanto, em vez de uma função regular, você faz melhor uso de useState construindo um gancho personalizado:

 function useUpdateUserAccount (updatedUserAccount) { const [userState, setUserState]=useState (nulo); useEffect (()=> { function handleStatusChange (user) { setUserState (usuário); } UserAPI.updateAccount (updatedUserAccount, handleUserChange); return ()=> { }; }, []); return userState;
}

Neste cenário, agora temos um ciclo de vida completo de uma função, graças a ganchos adicionais como useEffect . Isso agora pode ser usado como um gancho personalizado em diferentes componentes que você possa ter. Isso também pode ser o início da criação de sua própria loja, em vez de depender do Redux para casos de uso mais simples.

E não se esqueça de adicionar use como um prefixo para o nome da função para que você siga as regras dos ganchos!

Passando a função setState para componentes filho para definir o estado pai

Esta é basicamente a mesma má prática que passar useState no componente filho. Desta vez, estamos apenas passando a função setState para definir o estado de nosso componente pai.

Isso é possível fazer. Mas, é uma prática ruim e pode potencialmente introduzir efeitos colaterais indesejados ao longo do caminho, conforme o aplicativo é dimensionado.

Também não é fácil de ler e pode causar confusão, especialmente quando os componentes ficam emaranhados com casos de uso complicados.

Então, em vez de fazer isso:

 const NewComponent=({setValFn})=> { return (
); } const App=()=> { const [val, setVal]=useState (0); Retorna (

Valor: {val}



) }

Faça isto:

 const NewComponent=({onChangeValue})=> { return (
); } const App=()=> { const [val, setVal]=useState (0); const onValueChanged=(input)=> { setVal (entrada); } Retorna (

Valor: {val}



) }

Você está basicamente fazendo a mesma coisa de antes, onde pretendemos definir o estado de um componente pai. Só que desta vez, a última abordagem está emitindo eventos do componente filho para o componente pai. Em seguida, você deixa o componente pai definir o estado.

Não usando a destruição da matriz para usar useState

Você pode não estar ciente disso, mas pode usar useState desta forma:

 const count=useState [0];
const setCount=useState [1];

Isso ocorre porque ganchos como useState são na verdade uma matriz que retorna as seguintes implementações em cada elemento:

  1. Valor do estado inicializado: o valor que você passou em sua função. Pode ser um valor, uma string, um objeto, uma matriz, etc.)
  2. Função para definir o seu estado

Os documentos oficiais do React preferem que você use a desestruturação do array porque é mais limpo e fácil de ler sempre que você declara um gancho de estado. Além disso, eles usam a desestruturação de array, que se adequa ao seu caso de uso de declarar o estado com elegância.

Isso não significa que você está usando useState errado, mas não usar a desestruturação ES6 está tirando o açúcar sintático de como useState deve ser declarado, não mencionar que você também adicionou uma linha extra de código para declarar os dois.

Podemos ver a documentação do React oficial sobre como eles preferem useState a ser chamado usando a desestruturação do array ES6, assim:

 const [contagem, setCount]=useState (0);//Array desestruturando

Conclusão: agora pelo menos você sabe que pode usar useState em uma forma de array!

Contando apenas com useState para gerenciar o estado em aplicativos de grande escala

Não deve haver nenhum problema em confiar no useState para casos isolados na lógica do componente e casos de uso simples. Mas se todo o nosso aplicativo consiste apenas em useState para gerenciamento de estado, então podemos ter um problema a longo prazo devido às complexidades e um caso de uso envolvendo mais do que apenas dois componentes.

Os casos de uso que precisam mais do que apenas usar useState incluem:

  1. Se um estado for necessário em alguns componentes
  2. Se um aplicativo é dimensionado
  3. Se precisarmos de uma loja global

Se dependermos apenas do useState e passarmos o estado apenas para os adereços do componente, podemos acabar com o problema do “Prop Drilling”. Além disso, se vamos adicionar lógica relacionada à autenticação e segurança (o que será necessário para exigir a manutenção da sessão do usuário em um estado em algum ponto), precisaremos de um melhor gerenciamento de estado para armazenar e usar adequadamente o lógica em páginas diferentes que contêm componentes diferentes.

Bibliotecas de gerenciamento de estado, como Redux ou mesmo Context API, oferecem uma vantagem significativa em aplicativos de grande escala porque podem compartilhar o estado entre diferentes componentes. Eles geralmente vêm com ferramentas de navegador para rastrear qual estado está sendo passado em alguns componentes.

Isso torna mais fácil compartilhar e verificar a lógica por causa das ferramentas sofisticadas habilitadas com o uso de soluções de gerenciamento de estado, como Redux.

Então, para aplicativos e estado em grande escala, o que é necessário em vários componentes? Vá para o gerenciamento de estado, como Redux. Mas existem algumas soluções de gerenciamento de estado que você pode escolher, desde Flux ou apenas API de contexto.

Que tal usar ganchos personalizados? Possível. Mas, por segurança, é melhor confiar no Redux para casos de uso maiores.

Conclusão

O React agora é flexível graças aos Hooks. Mais especialmente, é graças a useState que não precisamos mais depender de componentes baseados em classe apenas para construir nossos componentes de IU.

A postagem Você está usando useState errado apareceu primeiro no LogRocket Blog .

Source link