Reagir ganchos - ganhar ou perder?

imagem


Com o lançamento do novo React 16.6.0, HOOKS (PROPOSAL) apareceu na documentação. Agora eles estão disponíveis no react 17.0.0-alpha e são discutidos no RFC aberto : React Hooks . Vamos ver o que é e por que é necessário sob o corte.


Sim, é RFC e você pode influenciar a implementação final, discutindo com os criadores do reag porque eles escolheram essa ou aquela abordagem.


Vamos dar uma olhada em como é um gancho padrão:


import { useState } from 'react'; function Example() { // Declare a new state variable, which we'll call "count" const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } 

Tente pensar nesse código, este é um teaser e, no final do artigo, você já entenderá o que significa. A primeira coisa que você deve saber é que isso não quebra a compatibilidade com versões anteriores e talvez elas sejam adicionadas na versão 16.7 após a coleta de comentários e sugestões na RFC.


Como os rapazes garantem, este não é um plano para eliminar as aulas de um reagente.


Além disso, ganchos não substituem os conceitos atuais da reação, tudo está no lugar de adereços / estado / contexto / refs. Esta é apenas outra maneira de usar seu poder.


Motivação


Os ganchos resolvem, à primeira vista, problemas não conectados que apareceram com o suporte de dezenas de milhares de componentes ao longo de 5 anos no facebook.


O mais difícil é reutilizar a lógica em componentes com estado, a reação não tem como anexar um comportamento reutilizável ao componente (por exemplo, conectá-lo ao repositório). Se você trabalhou com o React, conhece o conceito de HOC (componente de alta ordem) ou renderiza adereços. Esses padrões são bons o suficiente, mas às vezes são usados ​​excessivamente, exigem reestruturação dos componentes para que possam ser usados, o que geralmente torna o código mais complicado. Vale a pena examinar um aplicativo típico de reação e ficará claro o que está em jogo.


imagem


Isso é chamado de inferno envolto - inferno envolto .


Uma aplicação de HOCs sozinha é normal nas realidades atuais, eles conectaram o componente à loja / tema / localização / custom hock, acho que todo mundo sabe disso.


Torna-se claro que a reação precisa de outro mecanismo primitivo para separar a lógica.


Usando ganchos, podemos extrair o estado de um componente para que possa ser testado e reutilizado. Os ganchos permitem reutilizar a lógica do estado sem alterar a hierarquia dos componentes. Isso facilita a troca de links entre muitos componentes ou todo o sistema. Além disso, os componentes da classe parecem bastante assustadores, descrevemos os métodos do ciclo de vida de componentDidMount / shouldComponentUpdate / componentDidUpdate , o estado do componente, cria métodos para trabalhar com o estado / lado, vincula métodos para a instância do componente e, assim, pode continuar. Normalmente, esses componentes vão além das linhas x, onde x é difícil o suficiente para entender.


Os ganchos permitem fazer o mesmo dividindo a lógica entre componentes em pequenas funções e usando-os dentro dos componentes.


As aulas são difíceis para pessoas e carros


Ao observar as aulas no Facebook, é um grande obstáculo ao aprender o React. Você precisa entender como this funciona e, como em outras linguagens de programação, você também deve se lembrar sobre os manipuladores de eventos de ligação. Sem sentenças de sintaxe estáveis, o código parece muito detalhado. As pessoas entendem os padrões de adereços / estado e o chamado fluxo de dados de cima para baixo, mas as classes são bastante difíceis de entender.


Especialmente se não se limitando a modelos, há pouco tempo os caras da reação experimentaram o layout dos componentes com o Prepack e obtiveram resultados promissores, mas, mesmo assim, os componentes da classe permitem criar padrões indesejados indesejados que fazem essas otimizações desaparecerem, as classes também não migram muito bem quando As classes de recarga a quente tornam isso não confiável. Primeiro, os caras queriam fornecer uma API que suporta todas as otimizações e funciona bem com uma reinicialização a quente.


Dê uma olhada nos ganchos


Gancho de estado


O código abaixo renderiza um parágrafo e um botão e, se clicarmos no botão, o valor no parágrafo será incrementado.


 import { useState } from 'react'; function Example() { // Declare a new state variable, which we'll call "count" const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } 

A partir disso, podemos concluir que esse gancho funciona de maneira semelhante com um conceito como state .
Um método useState um pouco mais detalhado usa um argumento, esse é o valor padrão e retorna uma tupla na qual existe o valor em si e o método para alterá-lo, ao contrário de setState, setCount não fará mesclagem de valores, mas simplesmente atualize-o. Também podemos usar várias declarações de estado, por exemplo:


 function ExampleWithManyStates() { // Declare multiple state variables! const [age, setAge] = useState(42); const [fruit, setFruit] = useState('banana'); const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]); // ... } 

Assim, criamos vários estados ao mesmo tempo e não precisamos pensar em como decompô-los de alguma forma. Assim, pode-se distinguir que ganchos são funções que permitem "conectar-se" aos chips de componentes de classe, assim como ganchos não funcionam dentro de classes, é importante lembrar.


Gancho de efeito


Frequentemente, nos componentes da classe, criamos funções de efeito colateral, por exemplo, assinamos eventos ou fazemos solicitações de dados, geralmente para isso usamos os métodos componentDidUpdate / componentDidUpdate


 import { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); // Similar to componentDidMount and componentDidUpdate: useEffect(() => { // Update the document title using the browser API document.title = `You clicked ${count} times`; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } 

Quando chamamos useEffect , dizemos à reação para fazer um 'efeito colateral' após atualizar as alterações na árvore DOM. Os efeitos são declarados dentro do componente, portanto, eles têm acesso a props / state. Além disso, podemos criá-los exatamente da mesma maneira que você quiser.


 function FriendStatusWithCounter(props) { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); const [isOnline, setIsOnline] = useState(null); useEffect(() => { ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); function handleStatusChange(status) { setIsOnline(status.isOnline); } // ... 

Imediatamente, vale a pena prestar atenção ao segundo efeito colateral, retornamos a função, fazemos isso para executar algumas ações após a desmontagem do componente, na nova API isso é chamado de efeitos com a limpeza. Outros efeitos podem retornar qualquer coisa.


Regras de gancho


Ganchos são apenas funções javascript, mas requerem apenas duas regras:


  • Ganchos devem ser executados no topo da hierarquia de funções (isso significa que você não deve chamar ganchos em condições e loops, caso contrário, a reação não pode garantir a ordem de execução dos ganchos)
  • Ganchos de chamada apenas nas funções React ou componentes funcionais ou ganchos de chamada de ganchos personalizados (abaixo).
    Para seguir essas regras, os caras da equipe de reação criaram um plug-in de linter que gerará um erro se você chamar ganchos nos componentes da classe ou em loops e condições.

Ganchos personalizados


Ao mesmo tempo, queremos reutilizar a lógica dos componentes com estado, geralmente os padrões HOC ou de adereços de renderização são usados ​​para isso, mas eles criam volume adicional de nosso aplicativo.
Por exemplo, descrevemos a seguinte função:


 import { useState, useEffect } from 'react'; function useFriendStatus(friendID) { const [isOnline, setIsOnline] = useState(null); function handleStatusChange(status) { setIsOnline(status.isOnline); } useEffect(() => { ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange); }; }); return isOnline; } 

Perceba esse código, será um gancho personalizado que podemos chamar em vários componentes. Por exemplo, assim:


 function FriendStatus(props) { const isOnline = useFriendStatus(props.friend.id); if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline'; } 

mais ou menos


 function FriendListItem(props) { const isOnline = useFriendStatus(props.friend.id); return ( <li style={{ color: isOnline ? 'green' : 'black' }}> {props.friend.name} </li> ); } 

De qualquer forma, reutilizamos o estado do componente, cada chamada para a função useFriendStatus cria um estado isolado. Também é importante notar que o início desta função começa com a palavra use , o que significa que é um gancho. Recomendamos que você siga este formato. Você pode escrever ganchos personalizados para qualquer coisa, animações / assinaturas / temporizadores e muito mais.


Existem mais alguns ganchos.


useContext


useContext permite que você use o valor de retorno usual em vez de renderProps, o contexto que queremos recuperar nele e ele o retornará para nós, para que possamos nos livrar de todos os HOCs que passaram o contexto para adereços.


 function Example() { const locale = useContext(LocaleContext); const theme = useContext(ThemeContext); // ... } 

E agora podemos apenas usar o objeto de contexto no valor de retorno.


useCallback


 const memoizedCallback = useCallback( () => { doSomething(a, b); }, [a, b], ); 

Quantas vezes você teve que criar um componente de uma classe apenas para salvar uma referência a um método? Isso não precisa mais ser feito, podemos usar useCallback e nossos componentes não serão redesenhados porque um novo link para o onClick chegou.


useMemo


Retornamos o valor memorizado, o valor memorizado significa que ele é calculado apenas quando um dos argumentos mudou, na segunda vez que a mesma coisa não será calculada.


 const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]); 

Sim, aqui você deve duplicar os valores na matriz para que o gancho entenda que eles não foram alterados.


useRef


useRef retorna um valor .current , onde o campo .current será inicializado com o primeiro argumento, o objeto existirá enquanto o componente existir.
O exemplo mais comum ao se concentrar na entrada


 function TextInputWithFocusButton() { const inputEl = useRef(null); const onButtonClick = () => { // `current` points to the mounted text input element inputEl.current.focus(); }; return ( <> <input ref={inputEl} type="text" /> <button onClick={onButtonClick}>Focus the input</button> </> ); } 

useImperativeMethods


useImperativeMethods customiza o valor da instância que é passada do pai e usa ref diretamente. Como sempre, os links diretos devem ser evitados e o forwardRef deve ser usado


 function FancyInput(props, ref) { const inputRef = useRef(); useImperativeMethods(ref, () => ({ focus: () => { inputRef.current.focus(); } })); return <input ref={inputRef} ... />; } FancyInput = forwardRef(FancyInput); 

Neste exemplo, o componente que FancyInput pode chamar fancyInputRef.current.focus() .


useMutationEffect


useMutationEffect muito semelhante ao useEffect exceto que ele é iniciado de forma síncrona no estágio em que a reação altera os valores do DOM antes da atualização dos componentes vizinhos. Esse gancho deve ser usado para executar mutações no DOM.
É melhor preferir useEffect para impedir o bloqueio de alterações visuais.


useLayoutEffect


useLayoutEffect é semelhante ao useEffect exceto que é executado de forma síncrona após todas as atualizações do DOM e a nova renderização síncrona. As atualizações planejadas em useLayoutEffect são aplicadas de forma síncrona antes que o navegador possa desenhar elementos. Você também deve tentar usar o useEffect padrão para não bloquear alterações visuais.


useReducer


useReducer é um gancho para criar um redutor que retorna o estado e a capacidade de despachar alterações:


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

Se você entende como o Redux funciona, entende como o useReducer funciona. O mesmo exemplo que estava com o contador acima apenas através do useReducer :


 const initialState = {count: 0}; function reducer(state, action) { switch (action.type) { case 'reset': return initialState; case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; } } function Counter({initialCount}) { const [state, dispatch] = useReducer(reducer, initialState); return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'reset'})}> Reset </button> <button onClick={() => dispatch({type: 'increment'})}>+</button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> </> ); } 

UseReducer também recebe 3 argumentos, esta é a action que deve ser executada quando o redutor é inicializado:


 const initialState = {count: 0}; function reducer(state, action) { switch (action.type) { case 'reset': return {count: action.payload}; case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; } } function Counter({initialCount}) { const [state, dispatch] = useReducer( reducer, initialState, {type: 'reset', payload: initialCount}, ); return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'reset', payload: initialCount})}> Reset </button> <button onClick={() => dispatch({type: 'increment'})}>+</button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> </> ); } 

Também podemos criar um contexto nesse redutor e usá-lo através do gancho useContext usá-lo em todo o aplicativo. Isso permanece para trabalhos de casa.


Resumir


Hooks são uma abordagem bastante poderosa para resolver o inferno do invólucro e resolver vários problemas, mas todos eles podem ser usados ​​com uma definição de transferência de link . Já começam a aparecer coleções de ganchos para uso ou esta coleção . Você pode aprender mais sobre ganchos na documentação .

Source: https://habr.com/ru/post/pt428317/


All Articles