Desde que os ganchos apareceram no React, houve muitas perguntas sobre se eles podem substituir o Redux.
Acredito que ganchos e Redux têm pouco em comum. Hooks não nos dá novas e surpreendentes oportunidades de trabalhar com o estado. Em vez disso, eles estendem as APIs para que possam fazer no React o que já era possível nele. No entanto, a API do hook tornou o trabalho com os recursos de gerenciamento de estado do React muito mais conveniente. Verificou-se que é mais fácil usar os novos recursos para trabalhar com o estado do que os antigos que estavam disponíveis nos componentes com base nas classes. Agora eu uso as ferramentas para trabalhar com o estado dos componentes com muito mais frequência do que antes. Naturalmente, faço isso apenas quando é apropriado.

Para explicar minha atitude em relação ao React hooks e ao Redux, gostaria de falar primeiro sobre as situações em que o Redux geralmente é usado.
O que é o Redux?
Redux é uma biblioteca que implementa o armazenamento previsível do estado do aplicativo. É também uma arquitetura que se integra perfeitamente ao React.
Aqui estão os principais pontos fortes do Redux:
- Representação determinística do estado (em combinação com componentes puros, isso torna possível formar elementos visuais determinísticos).
- Suporte para alterações de estado transacional.
- Isolamento do gerenciamento de estado dos mecanismos de E / S e efeitos colaterais.
- A presença de uma única fonte de dados confiáveis para o estado.
- Fácil organização da colaboração com o estado em vários componentes.
- Ferramentas de análise transacional (registro automático de objetos de ação).
- Depuração com a capacidade de gravar e reproduzir o processo de execução do programa (Time Travel Debugging, TTD).
Em outras palavras, o Redux permite organizar bem seu código e depurá-lo convenientemente. O Redux ajuda a desenvolver aplicativos fáceis de manter. O uso desta biblioteca facilita a localização das fontes de problemas que surgem nos programas.
O que são ganchos React?
Os ganchos de reação permitem, ao trabalhar com componentes funcionais, usar um análogo do estado dos componentes com base em classes e análogos de seus métodos de ciclo de vida. Ganchos apareceram no React 16.8.
Entre os principais pontos fortes dos ganchos estão os seguintes:
- A capacidade de usar estado e manipular eventos de ciclo de vida de componentes sem usar componentes baseados em classe.
- Armazenamento conjunto da lógica relacionada no mesmo local do componente em vez de dividir uma lógica semelhante entre vários métodos de ciclo de vida.
- Mecanismos de compartilhamento independentes da implementação do componente (isso é semelhante ao modelo de suporte de renderização ).
Observe que esses ótimos recursos não sobrecarregam o Redux. Os ganchos do React podem e devem ser usados para executar atualizações de estado determinístico, mas esse sempre foi um dos recursos do React, e o modelo de estado determinístico do Redux se encaixa bem nesse recurso. É assim que o React alcança o determinismo na saída dos elementos visuais, e esse é, sem exagero, um dos motivos determinantes para a criação do React.
Se você usar ferramentas como
a API react-redux com suporte a gancho ou o
gancho React useReducer , descobrirá que não há motivo para perguntar o que escolher - hooks ou Redux. Você pode usar os dois, combinando e combinando essas tecnologias.
O que substitui os ganchos?
Após o advento das APIs de gancho, parei de usar as seguintes tecnologias:
O que não substitui os ganchos?
Eu ainda uso frequentemente as seguintes tecnologias:
- Redux - por todas as razões acima.
- Componentes de ordem superior - com o objetivo de executar a composição de componentes nos casos em que eu tenho que implementar a funcionalidade de ponta a ponta compartilhada por todos ou alguns dos componentes visuais do aplicativo. Essa funcionalidade inclui provedores Redux, sistemas de layout de página, sistemas de suporte a configurações de aplicativos, ferramentas de autenticação e autorização, ferramentas de internacionalização de aplicativos e assim por diante.
- Separação entre componentes de contêiner e componentes que têm uma representação visual. Isso permite melhorar a modularidade e a testabilidade dos aplicativos; é melhor separar efeitos e pura lógica.
Quando usar ganchos?
Não é necessário se esforçar para usar o Redux em todos os aplicativos e componentes. Se o seu projeto consistir em um componente visual, se ele não salvar dados e não carregar dados a partir dele, se operações de E / S assíncronas não forem executadas nele, não consigo pensar em um motivo digno para complicar esse projeto usando o Redux nele.
O mesmo pode ser dito sobre componentes que possuem os seguintes recursos:
- Eles não usam recursos de rede.
- Eles não armazenam dados no estado e não os carregam a partir daí.
- Eles não compartilham o estado com outros componentes que não são seus descendentes.
- Eles não têm um determinado estado próprio, usado para armazenamento de dados a curto prazo.
Você pode ter um bom motivo para usar o modelo padrão de estado do componente React em determinadas situações. Em tais situações, os ganchos React farão um bom trabalho. Por exemplo, o formulário descrito abaixo usa o estado local do componente usando o gancho React
useState
.
import React, { useState } from 'react'; import t from 'prop-types'; import TextField, { Input } from '@material/react-text-field'; const noop = () => {}; const Holder = ({ itemPrice = 175, name = '', email = '', id = '', removeHolder = noop, showRemoveButton = false, }) => { const [nameInput, setName] = useState(name); const [emailInput, setEmail] = useState(email); const setter = set => e => { const { target } = e; const { value } = target; set(value); }; return ( <div className="row"> <div className="holder"> <div className="holder-name"> <TextField label="Name"> <Input value={nameInput} onChange={setter(setName)} required /> </TextField> </div> <div className="holder-email"> <TextField label="Email"> <Input value={emailInput} onChange={setter(setEmail)} type="email" required /> </TextField> </div> {showRemoveButton && ( <button className="remove-holder" aria-label="Remove membership" onClick={e => { e.preventDefault(); removeHolder(id); }} > × </button> )} </div> <div className="line-item-price">${itemPrice}</div> <style jsx>{cssHere}</style> </div> ); }; Holder.propTypes = { name: t.string, email: t.string, itemPrice: t.number, id: t.string, removeHolder: t.func, showRemoveButton: t.bool, }; export default Holder;
Aqui
useState
usado para controlar o estado usado brevemente dos campos de entrada de
name
e
email
:
const [nameInput, setName] = useState(name); const [emailInput, setEmail] = useState(email);
Você pode perceber que ainda existe um criador da ação
removeHolder
que entra nas propriedades do Redux. Como já mencionado, a combinação e combinação de tecnologias é completamente normal.
Usar o estado local de um componente para resolver esses problemas sempre pareceu bom, mas antes dos ganchos do React, eu queria, em qualquer caso, salvar os dados do componente no armazenamento Redux e obter o estado das propriedades.
Anteriormente, trabalhando com o estado de um componente envolvido usando componentes baseados em classe, gravando os dados iniciais no estado usando os mecanismos para declarar propriedades de classe (ou no construtor de classe) e assim por diante. Como resultado, verificou-se que, para evitar o uso do Redux, o componente tinha que ser muito complicado. O Redux também falou a favor da existência de ferramentas convenientes para gerenciar o estado dos formulários usando o Redux. Como resultado, antes, eu não teria me preocupado com o fato de o estado temporário do formulário ser armazenado no mesmo local que os dados com uma vida útil mais longa.
Como já usei o Redux em todas as minhas aplicações mais ou menos complexas, a escolha da tecnologia para armazenar o estado dos componentes dos componentes não me causou muita reflexão. Eu apenas usei o Redux em quase todos os casos.
Em condições modernas, fazer uma escolha também é fácil: trabalhar com o estado do componente é organizado usando mecanismos padrão do React e gerenciando o estado do aplicativo usando o Redux.
Quando usar o Redux?
Outra pergunta comum sobre gerenciamento de estado é: “Preciso colocar absolutamente tudo no repositório Redux? Se não o fizer, ele violará a capacidade de depurar aplicativos usando mecanismos TTD? "
Não há necessidade de hospedar absolutamente tudo no repositório Redux. O fato é que os aplicativos usam muitos dados temporários dispersos demais para fornecer algumas informações que, sendo registradas no log ou usadas durante a depuração, podem fornecer ao desenvolvedor uma ajuda significativa para encontrar problemas. Provavelmente você, a menos que esteja escrevendo um aplicativo de editor em tempo real, não precisará gravar no estado todo movimento do mouse ou pressionamento de tecla. Ao colocar algo em um estado Redux, você adiciona um nível adicional de abstração ao aplicativo, além de um nível adicional de complexidade que o acompanha.
Em outras palavras, você pode usar o Redux com segurança, mas deve haver algum motivo para isso. O uso dos recursos Redux nos componentes pode ser justificado se os componentes diferirem nos seguintes recursos:
- Eles usam E / S. Por exemplo, eles trabalham com uma rede ou com determinados dispositivos.
- Eles salvam dados ou carregam dados deles.
- Eles trabalham com seu estado em conjunto com componentes que não são seus descendentes.
- Eles lidam com qualquer lógica comercial com a qual outras partes do aplicativo lidam - eles processam dados que são usados em outras partes do aplicativo.
Aqui está outro exemplo retirado do aplicativo
TDDDay :
import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { compose } from 'ramda'; import page from '../../hocs/page.js'; import Purchase from './purchase-component.js'; import { addHolder, removeHolder, getHolders } from './purchase-reducer.js'; const PurchasePage = () => {
Este documento não lida com o DOM. Este é um componente de apresentação. Ele está conectado ao Redux usando
a API react-redux com suporte a gancho .
O redux é usado aqui porque precisamos que os dados que este formulário manipule sejam usados em outras partes da interface do usuário. E após a conclusão da operação de compra, precisamos salvar as informações relevantes no banco de dados.
Os fragmentos de estado com os quais esse código trabalha são usados por vários componentes; eles não são processados por apenas um componente. Não são dados, existindo apenas um curto período de tempo. Esses dados podem ser considerados permanentes, podem ser usados em diferentes telas do aplicativo e em várias sessões. Todos esses são cenários nos quais os estados do componente para armazenamento de dados não podem ser aplicados. É verdade que isso ainda é possível, mas apenas se o criador do aplicativo gravar, com base na API do React, sua própria biblioteca para gerenciar o estado. Isso é muito mais difícil do que apenas usar o Redux.
A API do React
Suspense , no futuro, pode ser útil ao armazenar dados no estado e carregá-los a partir dele. Precisamos aguardar seu lançamento e ver se ele pode substituir os modelos para salvar e carregar dados Redux. O Redux nos permite separar claramente os efeitos colaterais do restante da lógica do componente, enquanto não precisamos trabalhar com serviços de E / S de uma maneira especial. (O motivo pelo qual prefiro a biblioteca
redux-saga ao
middleware redux-thunk é o isolamento do efeito). Para competir com o Redux nesse cenário, a API do React precisará fornecer isolamento de efeito.
Redux é arquitetura
O Redux é muito mais (e geralmente muito menos) do que uma biblioteca de gerenciamento de estado. É também um subconjunto da arquitetura
Flux , que define muito mais estritamente como as mudanças de estado são implementadas. Leia mais sobre a arquitetura Redux
aqui .
Costumo usar redutores criados no estilo Redux nesses casos em que preciso manter o estado complexo do componente, mas não preciso usar a biblioteca Redux. Eu também uso ações criadas no espírito do Redux (e até mesmo ferramentas do Redux como
Autodux e
redux-saga ) para enviar ações para aplicativos Node.js. No entanto, nem importo o Redux para esses aplicativos.
O projeto Redux sempre foi mais uma arquitetura e um conjunto de acordos voluntários do que uma biblioteca. De fato, a implementação básica do Redux pode ser apresentada literalmente em algumas dezenas de linhas de código.
Isso acabará sendo uma boa notícia para quem deseja usar o estado local dos componentes com ganchos com mais frequência e não vincula tudo ao Redux.
O React suporta o gancho
useReducer
, que pode funcionar com redutores no estilo Redux. Isso é bom para implementar uma lógica não trivial de trabalhar com o estado, trabalhar com fragmentos de estado dependentes e assim por diante. Se você encontrar um problema para o qual o estado temporário de um componente individual é adequado, poderá usar a arquitetura Redux para trabalhar com esse estado, mas, em vez da biblioteca Redux, poderá usar o gancho
useReducer
para gerenciar o estado.
Se, posteriormente, você precisar estabelecer um armazenamento permanente de dados que anteriormente só foram armazenados temporariamente, estará 90% pronto para essa alteração. Tudo o que você precisa fazer é conectar o componente ao repositório Redux e adicionar o redutor correspondente lá.
Perguntas e Respostas
Determin O determinismo está quebrado se o Redux não gerencia todos os dados do aplicativo?
Não, não está quebrado. De fato, o uso do Redux não torna um projeto determinístico. Mas os acordos são. Se você deseja que seu estado Redux seja determinístico, use
funções puras . O mesmo se aplica a situações nas quais é necessário que o estado temporário dos componentes locais seja determinado.
▍ A biblioteca Redux deve desempenhar o papel de uma única fonte de dados confiáveis?
O princípio de uma única fonte de dados confiáveis não indica que é necessário que todos os dados incluídos no estado do aplicativo sejam armazenados em um único local. O significado desse princípio é que cada fragmento do estado deve ter apenas uma fonte de dados confiáveis. Como resultado, podemos ter muitos fragmentos de estado, cada um dos quais com sua própria fonte de dados confiáveis.
Isso significa que o programador pode decidir o que é transferido para o Redux e o que é transferido para o estado dos componentes. Dados determinantes do estado também podem ser obtidos de outras fontes. Por exemplo, em uma API do navegador que permite trabalhar com informações sobre o endereço da página que você está visualizando.
O Redux é uma ótima ferramenta para oferecer suporte a uma única fonte de dados confiáveis para o estado de um aplicativo. Porém, se o estado do componente estiver localizado e usado exclusivamente dentro desse componente, então, por definição, esse estado já terá uma única fonte de dados confiáveis - o estado do componente React.
Se você colocar alguns dados no estado Redux, sempre deve ler esses dados no estado Redux. Para tudo o que há no repositório Redux, esse repositório deve ser a única fonte de dados confiáveis.
Colocar tudo em um estado Redux, se necessário, é perfeitamente normal. Talvez isso afete o desempenho se você usar fragmentos de estado que precisam ser atualizados com freqüência ou se estiver falando sobre o armazenamento do estado de um componente no qual os fragmentos de estado dependentes são muito usados. Você não deve se preocupar com desempenho até que haja problemas com o desempenho. Mas se você estiver preocupado com a questão do desempenho, tente as duas maneiras de trabalhar com o estado e avalie seu impacto no desempenho. Crie um perfil do seu projeto e lembre-se do modelo de desempenho RAIL.
▍ Preciso usar a função de conexão do react-redux, ou é melhor usar ganchos?
Depende muito. A função de
connect
cria um componente de ordem superior adequado para uso repetido, e os ganchos são otimizados para integração com um único componente.
Preciso conectar as mesmas propriedades a diferentes componentes? Nesse caso, use
connect
. Caso contrário, eu preferiria pegar ganchos. Por exemplo, imagine que você tenha um componente responsável por autorizar permissões para ações do usuário:
import { connect } from 'react-redux'; import RequiresPermission from './requires-permission-component'; import { userHasPermission } from '../../features/user-profile/user-profile-reducer'; import curry from 'lodash/fp/curry'; const requiresPermission = curry( (NotPermittedComponent, { permission }, PermittedComponent) => { const mapStateToProps = state => ({ NotPermittedComponent, PermittedComponent, isPermitted: userHasPermission(state, permission), }); return connect(mapStateToProps)(RequiresPermission); }, ); export default requiresPermission;
Agora, se um administrador estiver trabalhando intensamente com o aplicativo, cujas ações precisam de permissão especial, você poderá criar um componente de ordem superior que combine todas essas permissões com toda a funcionalidade de ponta a ponta necessária:
import NextError from 'next/error'; import compose from 'lodash/fp/compose'; import React from 'react'; import requiresPermission from '../requires-permission'; import withFeatures from '../with-features'; import withAuth from '../with-auth'; import withEnv from '../with-env'; import withLoader from '../with-loader'; import withLayout from '../with-layout'; export default compose( withEnv, withAuth, withLoader, withLayout(), withFeatures, requiresPermission(() => <NextError statusCode={404} />, { permission: 'admin', }), );
Veja como usá-lo:
import compose from 'lodash/fp/compose'; import adminPage from '../HOCs/admin-page'; import AdminIndex from '../features/admin-index/admin-index-component.js'; export default adminPage(AdminIndex);
A API do componente de ordem superior é conveniente para esta tarefa. Ele permite que você o resolva de forma mais sucinta, usando menos código do que ganchos. Mas, para usar a função de
connect
, lembre-se de que ela leva
mapStateToProps
como o primeiro argumento e
mapStateToProps
como o segundo. Não devemos esquecer que esta função pode assumir funções ou objetos literais. Você precisa saber como os diferentes usos da
connect
diferem e se essa é uma função ao curry, mas seu curry não é realizado automaticamente.
Em outras palavras, posso dizer que acredito que, durante o desenvolvimento do
connect
muito trabalho foi feito na direção da concisão do código, mas o código resultante não é particularmente legível nem conveniente. Se não precisar trabalhar com vários componentes, preferirei com prazer a API de
connect
inconveniente a uma API de gancho muito mais conveniente, mesmo considerando que isso levará a um aumento na quantidade de código.
▍ Se um singleton é considerado um antipadrão e o Redux é um singleton, isso significa que o Redux é um antipadrão?
Não, não é. O uso de um singleton no código sugere a qualidade duvidosa desse código, indicando a presença de um estado mutável compartilhado nele. Este é um verdadeiro anti-padrão. O Redux, por outro lado, evita a mutação do estado compartilhado através do encapsulamento (você não deve alterar o estado do aplicativo diretamente, fora dos redutores; o Redux resolve o problema de alterar o estado) e enviando mensagens (apenas o objeto de evento enviado pode causar uma alteração de estado).
Sumário
O Redux substitui os ganchos React? Ganchos são ótimos, mas não substituem o Redux.
Esperamos que este material o ajude a escolher um modelo de gerenciamento de estado para seus projetos React.
Caros leitores! Você já encontrou situações em que os ganchos do React podem substituir o Redux?
