Redux: Quais são as desvantagens de armazenar todo o seu estado em um único átomo imutável?

Criado em 10 fev. 2016  ·  91Comentários  ·  Fonte: reduxjs/redux

Eu entendo que este é o princípio subjacente a todo redux, e que ele vem com todos esses benefícios incríveis que são bastante conhecidos agora.

No entanto, eu sinto que um ponto em que o redux está faltando é que ele não descreve abertamente as desvantagens conceituais de usar essa arquitetura. Talvez esta seja uma boa escolha, pois você deseja que as pessoas não se assustem devido aos pontos negativos.

Estou apenas curioso porque não se aplica apenas a redux, mas de alguma forma a outras coisas como Om , datomic e aquela conversa sobre virar o banco de dados do avesso . Gosto de redux, gosto de Om, me interesso por datomic e gostei muito dessa palestra.

Mas, apesar de todas as pesquisas que fiz, é difícil encontrar pessoas que critiquem a loja única e imutável. Este é um exemplo, mas parece ter um problema mais com a verbosidade do redux do que com a arquitetura real.

A única coisa que consigo pensar é que talvez seja necessário mais memória para manter o armazenamento de cópias de seu estado, ou que seja um pouco mais difícil fazer um protótipo rapidamente com redux.

Como vocês provavelmente pensaram muito mais nisso do que eu, poderiam ajudar a elucidar isso para mim?

discussion docs question

Comentários muito úteis

Redux é basicamente fonte de eventos onde há uma única projeção para consumir.

Em sistemas distribuídos, geralmente há um log de eventos (como Kafka) e vários consumidores que podem projetar / reduzir esse log em vários bancos de dados / armazenamentos hospedados em servidores diferentes (normalmente, uma réplica de banco de dados é, na verdade, um redutor). Então, em um mundo distribuído, a carga e o uso de memória são ... distribuídos, enquanto no Redux, se você tiver centenas de widgets que têm seu estado local, tudo isso é executado em um único navegador, e cada mudança de estado tem alguma sobrecarga devido ao imutável atualizações de dados.

Na maioria dos casos, essa sobrecarga não é um grande problema, no entanto, ao montar entradas de texto em um estado não muito bom e digitar rápido em um dispositivo móvel lento, nem sempre tem desempenho suficiente.

Renderizar esse estado de cima para baixo não é, na minha experiência, conveniente e nem sempre tem desempenho suficiente (pelo menos com o React, que provavelmente não é a implementação mais rápida do VDom). Redux resolve isso com connect mas ainda assim, conforme o número de conexões aumenta, pode se tornar um gargalo. Existem soluções

Além disso, estruturas de dados persistentes como ImmutableJS nem sempre oferecem os melhores desempenhos em algumas operações, como adicionar um item em um índice aleatório em uma grande lista (veja minha experiência em renderizar grandes listas aqui )

Redux inclui o log de eventos e a projeção porque é conveniente e está ok para a maioria dos casos de uso, mas poderia ser possível manter um log de eventos fora do redux e projetá-lo em 2 ou mais redux store, adicionar todos esses redux store a reage ao contexto em chaves diferentes e tem menos sobrecarga especificando a qual armazenamento queremos nos conectar. Isso é absolutamente possível, no entanto, tornaria a API e os devtools mais difíceis de implementar, pois agora você precisa de uma distinção clara entre o armazenamento e o log de eventos.


Eu também concordo com @jquense

Os benefícios de hidratação fácil, instantâneos, viagem no tempo, etc, só funcionam se não houver outro lugar onde um estado importante vive. No contexto do React, isso significa que você precisa armazenar o estado, que pertence apropriadamente aos componentes na Loja, ou perderá vários benefícios. Se você quiser colocar tudo em Redux, muitas vezes acaba sendo pesado e prolixo, e adiciona um nível irritante de indireção.

Montar qualquer estado na loja Redux requer mais clichês. A arquitetura do Elm provavelmente resolve isso de uma maneira mais elegante, mas também requer muitos clichês.

Mas também não é possível ou eficaz tornar tudo controlado pelo estado. Às vezes, usamos bibliotecas existentes para as quais é difícil construir uma interface declarativa. Alguns estados também são difíceis de montar para reduxar armazenamento de maneira eficiente, incluindo:

  • Entradas de texto
  • Posição de rolagem
  • Tamanho da janela de visualização
  • Posição do mouse
  • Posição / seleção do cursor
  • DataUrls da tela
  • Estado não serializável
  • ...

Todos 91 comentários

Essa é uma ótima pergunta. Tentarei escrever uma resposta abrangente amanhã, mas gostaria de ouvir alguns de nossos usuários primeiro. Seria bom resumir essa discussão como uma página de documento oficial mais tarde. Não fizemos isso no início porque não conhecíamos as desvantagens.

Muito obrigado! Eu realmente queria ouvir sobre isso.

Aliás, incentivo a todos que desejam contribuir com essa discussão a também dar uma ideia do que estão construindo com o Redux para que seja mais fácil entender o escopo dos projetos.

É um tipo de pergunta difícil b / c projetos como OM e redux e outras bibliotecas de átomo de estado único, ativamente mitigar as desvantagens. Sem a restrição de imutabilidade, e acesso controlado e fechado, um átomo de estado único não é nada diferente de anexar todos os seus dados ao window (cujas desvantagens são bem conhecidas)

Especificamente para a abordagem Redux, porém, a maior desvantagem para nós é que os átomos de estado único são uma espécie de tudo ou nada. Os benefícios de uma hidratação fácil, instantâneos, viagem no tempo, etc. só funcionam se não houver nenhum outro lugar importante. No contexto do React, isso significa que você precisa armazenar o estado, que pertence apropriadamente aos componentes na Loja, ou perderá vários benefícios. Se você quiser colocar tudo em Redux, muitas vezes acaba sendo pesado e prolixo, e adiciona um nível irritante de indireção.

No entanto, nenhuma dessas desvantagens tem sido particularmente proibitiva para nós :)

Então, tenho estudado o Redux, e esse estilo de arquitetura, para modelar estados de jogo; Tenho interesse em criar mundos simulados no estilo Civ e histórias de jogos de tabuleiro totalmente registradas de forma mais simples, em um contexto de navegador, o que talvez seja um caso de uso incomum para alguns, mas duvido que alguém queira que eu pare também.

Nesse contexto, estou interessado em esforços para gerenciar o tamanho da estrutura de dados do histórico, para mover partes dela para o servidor ou disco para salvá-la, restaurando segmentos específicos, etc.

O que mais tenho lutado, como novo usuário, é tentar superar a mudança extrema (imho) de estilo de codificação no Redux e as mudanças conceituais ao mesmo tempo; continua um pouco assustador mesmo agora, um mês e muda depois de tentar mergulhar fundo nele; Estou olhando https://github.com/ngrx/store a seguir para obter os conceitos, mas com um estilo / sintaxe mais familiar e mais do resto da estrutura implícita / fornecida.

Eu entendo que, para alguns, uma estrutura é uma muleta; mas alguns de nós precisam disso; poucas pessoas estarão no topo de seu jogo o suficiente para ter opiniões fortes úteis, então frameworks 'são melhores do que tatear no escuro, sabe? Então, esse é um ponto meu, um programador de nível médio, de meia-idade, novo nos conceitos :)

Basicamente, o Redux e seus vídeos me ensinaram como fazer em javascript puro, mas sendo um programador menos experiente, ainda não tenho ideia de como entregar um produto sem orientação adicional, então até ver aquele exemplo de uma coisa acabada, eu apenas fico parado como:

Não é sua culpa, mas ainda é um problema que eu adoraria ajudar a resolver :)

Acho que a maior dúvida que ainda tenho neste ponto é: para onde devem ir todos os itens não serializáveis, como funções, instâncias ou promessas? Estava me perguntando sobre isso em Reactiflux na outra noite e não obtive nenhuma boa resposta. Também vi alguém postar http://stackoverflow.com/questions/35325195/should-i-store-function-references-in-redux-store .

Caso de uso para várias lojas (lembre-se de que esta é a melhor que eu poderia imaginar):

Um aplicativo que combina vários sub-aplicativos juntos. Pense em algo como o My Yahoo ou outro produto de página inicial personalizável. Cada widget precisará manter seu próprio estado sem sobreposição entre os dois. Uma equipe de produto é provavelmente atribuída a cada widget, portanto, eles podem não saber os efeitos do código de outras pessoas no ambiente.

Reunidas, você provavelmente pode conseguir isso no Redux violando algumas regras e tomando cuidado ao propagar essas lojas em algo como o contexto do React. Mas pode ser mais fácil com uma estrutura que lida com vários átomos de estado por padrão.

@gaearon estamos construindo uma plataforma de negociação. Eu já expliquei a você uma vez por que uma loja não serve para nós (após o encontro React.js em São Petersburgo). Vou tentar explicar novamente em detalhes (na postagem do blog no meio?), Mas provavelmente preciso de ajuda devido às minhas habilidades em inglês :) Tudo bem se eu enviar para você ou outra pessoa aqui para análise no Twitter direto mensagens quando será feito? Não posso citar a data exata, mas vou tentar escrever este post em breve (provavelmente no final deste mês).

E sim, ainda estamos usando Redux com várias lojas, violando algumas regras de documentos como @timdorr disse (com o custo disso, não podemos usar react-redux tão confortavelmente em casos em que precisamos de dados de várias lojas como estão no caso de loja única)

Redux é basicamente fonte de eventos onde há uma única projeção para consumir.

Em sistemas distribuídos, geralmente há um log de eventos (como Kafka) e vários consumidores que podem projetar / reduzir esse log em vários bancos de dados / armazenamentos hospedados em servidores diferentes (normalmente, uma réplica de banco de dados é, na verdade, um redutor). Então, em um mundo distribuído, a carga e o uso de memória são ... distribuídos, enquanto no Redux, se você tiver centenas de widgets que têm seu estado local, tudo isso é executado em um único navegador, e cada mudança de estado tem alguma sobrecarga devido ao imutável atualizações de dados.

Na maioria dos casos, essa sobrecarga não é um grande problema, no entanto, ao montar entradas de texto em um estado não muito bom e digitar rápido em um dispositivo móvel lento, nem sempre tem desempenho suficiente.

Renderizar esse estado de cima para baixo não é, na minha experiência, conveniente e nem sempre tem desempenho suficiente (pelo menos com o React, que provavelmente não é a implementação mais rápida do VDom). Redux resolve isso com connect mas ainda assim, conforme o número de conexões aumenta, pode se tornar um gargalo. Existem soluções

Além disso, estruturas de dados persistentes como ImmutableJS nem sempre oferecem os melhores desempenhos em algumas operações, como adicionar um item em um índice aleatório em uma grande lista (veja minha experiência em renderizar grandes listas aqui )

Redux inclui o log de eventos e a projeção porque é conveniente e está ok para a maioria dos casos de uso, mas poderia ser possível manter um log de eventos fora do redux e projetá-lo em 2 ou mais redux store, adicionar todos esses redux store a reage ao contexto em chaves diferentes e tem menos sobrecarga especificando a qual armazenamento queremos nos conectar. Isso é absolutamente possível, no entanto, tornaria a API e os devtools mais difíceis de implementar, pois agora você precisa de uma distinção clara entre o armazenamento e o log de eventos.


Eu também concordo com @jquense

Os benefícios de hidratação fácil, instantâneos, viagem no tempo, etc, só funcionam se não houver outro lugar onde um estado importante vive. No contexto do React, isso significa que você precisa armazenar o estado, que pertence apropriadamente aos componentes na Loja, ou perderá vários benefícios. Se você quiser colocar tudo em Redux, muitas vezes acaba sendo pesado e prolixo, e adiciona um nível irritante de indireção.

Montar qualquer estado na loja Redux requer mais clichês. A arquitetura do Elm provavelmente resolve isso de uma maneira mais elegante, mas também requer muitos clichês.

Mas também não é possível ou eficaz tornar tudo controlado pelo estado. Às vezes, usamos bibliotecas existentes para as quais é difícil construir uma interface declarativa. Alguns estados também são difíceis de montar para reduxar armazenamento de maneira eficiente, incluindo:

  • Entradas de texto
  • Posição de rolagem
  • Tamanho da janela de visualização
  • Posição do mouse
  • Posição / seleção do cursor
  • DataUrls da tela
  • Estado não serializável
  • ...

Esta pergunta que acabei de ver no SO:

http://stackoverflow.com/questions/35328056/react-redux-should-all-component-states-be-kept-in-redux-store

Echos alguma confusão que vi sobre o gerenciamento do estado da IU e se o estado da IU deve pertencer ao componente ou ir para a loja. # 1287 oferece uma boa resposta a essa pergunta, mas não é tão aparente no início e pode estar em debate.

Além disso, isso pode ser um obstáculo se você estiver tentando implementar algo como este https://github.com/ericelliott/react-pure-component-starter em que cada componente é puro e não tem voz em seu próprio estado .

Achei difícil usar a árvore de estado único do redux quando precisava gerenciar dados de várias sessões. Eu tinha um número arbitrário de ramos com uma forma idêntica e cada um tinha vários identificadores exclusivos (dependendo de onde os dados vieram, você usaria uma chave diferente). A simplicidade das funções do redutor e das funções de nova seleção desapareceu rapidamente quando confrontada com esses requisitos - ter que escrever getters / setters personalizados para direcionar um branch específico parecia excessivamente complexo e plano em um ambiente simples e compossível. Não sei se vários armazenamentos são a melhor opção para atender a esse requisito, mas algumas ferramentas em torno do gerenciamento de sessões (ou quaisquer outros dados de formato idêntico) em uma única árvore de estado seria bom.

Maior probabilidade de colisões de chaves de estado entre redutores.
As mutações e declarações de dados estão longe do código onde os dados são usados ​​(quando escrevemos um código, tentamos colocar declarações de variáveis ​​perto do local onde as usamos)

Deixe-me começar com meu caso de uso específico: estou usando Redux com virtual-dom, onde todos os meus componentes de IU são puramente funcionais e não há como ter estado local para vários componentes.

Com isso dito, definitivamente a parte mais difícil é amarrar no estado de animação . Os exemplos a seguir têm o estado de animação em mente, mas muito disso pode ser generalizado para o estado da IU .

Algumas razões pelas quais o estado de animação é estranho para Redux:

  • Os dados da animação mudam com frequência
  • Ações específicas de animação poluem o histórico de ações em seu aplicativo, tornando difícil reverter ações de uma forma significativa
  • A árvore de estado Redux começa a espelhar a árvore de componentes se houver muitas animações específicas de componentes
  • Mesmo o estado básico de animação como animationStarted ou animationStopped começa a acoplar o estado à sua IU.

Por outro lado, definitivamente existem desafios para construir o estado de animação inteiramente fora da árvore de estado Redux.

Se você tentar fazer animações manipulando o DOM, deve estar atento a coisas como estas:

  • Os diffs do dom virtual não levarão em consideração a manipulação da sua animação, como estilos personalizados que você definiu no nó — se você definir alguns estilos em um nó DOM, você mesmo terá que removê-los se quiser que eles desapareçam
  • Se você realizar animações no próprio documento, deve estar ciente das atualizações do dom virtual - você pode iniciar uma animação em um nó DOM, apenas para descobrir que o conteúdo desse nó DOM foi alterado no meio da animação
  • Se você executar animações no próprio documento, deve ter cuidado com os estilos e atributos de sobrescrita do dom virtual definidos em seu nó e também com os estilos de sobrescrita e atributos definidos pelo dom virtual

E se você tentar deixar o virtual-dom cuidar de toda a manipulação do DOM (o que você deve!), Mas sem manter o estado de animação no Redux, você acabará com estes problemas difíceis como estes:

  • Como você expõe o estado de animação aos seus componentes? Estado local em seus componentes? Algum outro estado global?
  • Normalmente, sua lógica de estado está em redutores Redux, mas agora você precisa adicionar muita lógica de renderização diretamente em seus componentes para saber como eles serão animados em resposta ao seu estado. Isso pode ser bastante desafiador e prolixo.

Atualmente, existem projetos impressionantes, como o react-motion, que fazem grandes avanços na resolução deste problema _para o React_, mas Redux não é exclusivo do React. Nem deveria ser, eu sinto - muitas pessoas estão trazendo sua própria camada de visão e tentando se integrar com o Redux.

Para quem estiver curioso, a melhor solução que encontrei para Redux + virtual-dom é manter dois átomos de estado: Redux mantém o estado do aplicativo principal, mantém a lógica principal para manipular esse estado em resposta às ações, etc. O outro estado é um objeto mutável que mantém o estado de animação (eu uso mobservable). O truque, então, é assinar as mudanças de estado do Redux _and_ as mudanças de estado de animação, e renderizar a IU como uma função de ambos:

/* Patch h for jsx/vdom to convert <App /> to App() */
import h from './h'
import { diff, patch, create } from 'virtual-dom'
import { createStore } from 'redux'
import { observable, autorun } from 'mobservable'
import TWEEN from 'tween.js'
import rootReducer from './reducers'
import { addCard } from './actions'
import App from './containers/App'

// Redux state
const store = createStore()

// Create vdom tree
let tree = render(store.getState())
let rootNode = create(tree)
document.body.appendChild(rootNode)

// Animation observable
let animationState = observable({
  opacity: 0
})

// Update document when Redux state 
store.subscribe(function () {
  // ... anything you need to do in response to Redux state changes
  update()
})

// Update document when animation state changes
autorun(update)

// Perform document update with current state
function update () {
  const state = store.getState()
  let newTree = render(state, animationState)
  let patches = diff(tree, newTree)
  rootNode = patch(rootNode, patches)
  tree = newTree
}

// UI is a function of current state (and animation!)
function render (state, animation = {}) {
  return (
    <App {...state} animation={animationState} />
  )
}

// Do some animations
function animationLoop (time) {
  window.requestAnimationFrame(animationLoop)
  TWEEN.update(time)
}
animationLoop()

new TWEEN.Tween(animationState)
      .to({ opacity: 100 }, 300)
      .start()

// Or when you dispatch an action, also kick off some animation changes...
store.dispatch(addCard())
/* etc... */

Obrigado a todos por ótimas respostas! Continue vindo.

Uma coisa a notar é que não pretendemos que o Redux seja usado para _todos_ o estado. Tudo o que parece significativo para o aplicativo. Eu diria que as entradas e o estado de animação devem ser tratados pelo React (ou outra abstração de estado efêmera). Redux funciona melhor para coisas como dados buscados e modelos modificados localmente.

@taggartbg

Achei difícil usar a árvore de estado único do redux quando precisava gerenciar dados de várias sessões. Eu tinha um número arbitrário de ramos com uma forma idêntica e cada um tinha vários identificadores exclusivos (dependendo de onde os dados vieram, você usaria uma chave diferente). A simplicidade das funções do redutor e das funções de nova seleção desapareceu rapidamente quando confrontada com esses requisitos - ter que escrever getters / setters personalizados para direcionar um branch específico parecia excessivamente complexo e plano em um ambiente simples e compossível.

Você se importaria de criar um problema que descreva seu caso de uso com mais detalhes? Pode ser que haja uma maneira simples de organizar a forma do estado de maneira diferente que você está perdendo. Em geral, vários ramos com o mesmo formato de estado, mas gerenciados por redutores diferentes, é um anti-padrão.

@istarkov

Maior probabilidade de colisões de chaves de estado entre redutores.

Você se importaria de explicar como isso acontece com mais detalhes? Normalmente, sugerimos que você execute apenas um único redutor em qualquer fatia de estado. Como podem acontecer as colisões? Você está fazendo várias passagens sobre o estado? Se sim, por quê?

@gaearon @istarkov : talvez o que quis dizer é que vários plug-ins e bibliotecas relacionadas podem estar disputando o mesmo namespace de nível superior? A Biblioteca A quer uma chave superior chamada "myState", mas a Biblioteca B também, etc.

Sim, este é um bom ponto, mesmo que não seja isso que @istarkov quis dizer. (Não sei.) Em geral, as bibliotecas devem oferecer uma maneira de montar o redutor em qualquer lugar da árvore, mas sei que algumas bibliotecas não permitem isso.

@gaearon

Você se importaria de criar um problema que descreva seu caso de uso com mais detalhes? Pode ser que haja uma maneira simples de organizar a forma do estado de maneira diferente que você está perdendo. Em geral, vários ramos com o mesmo formato de estado, mas gerenciados por redutores diferentes, é um anti-padrão.

Eu ficaria feliz! Farei isso quando tiver um momento.

Acho que é definitivamente considerado um antipadrão, embora eu tenha um único redutor compartilhado para gerenciar esses ramos. No entanto, sinto que o paradigma que redux fornece não está longe de ser um caso de uso perfeito para serializar / desserializar branches idênticos como estado imutável. Não vejo por que seria contra o ethos de redux construir algo como selecionar novamente com alguma lógica assumida para direcionar ramificações específicas por alguma chave.

Eu ficaria feliz em conversar sobre isso fora do tópico, posso muito bem estar errado.

@taggartbg : falando inteiramente sem saber como é o seu código aqui. Você está falando sobre tentar lidar com um estado parecido com este?

{ groupedData : { first : {a : 1, b : 2}, second : {a : 3, b : 4}, third : {a : 5, b, 6} }

Parece que você poderia lidar com isso no lado do redutor, tendo uma única função de redutor que usa a chave de ID por grupo como parte de cada ação. Na verdade, você já olhou para algo como https://github.com/erikras/multireducer , https://github.com/lapanoid/redux-delegator , https://github.com/reducks/redux-multiplex ou https://github.com/dexbol/redux-register?

Além disso, no lado da nova seleção, o fato de que React-Redux agora oferece suporte a seletores por componente pode ajudar, uma vez que melhora os cenários em que você está fazendo a seleção com base em props de componente.

@gaearon

Uma coisa a notar é que não pretendemos que Redux seja usado para todos os estados. Tudo o que parece significativo para o aplicativo. Eu diria que as entradas e o estado de animação devem ser tratados pelo React (ou outra abstração de estado efêmera). Redux funciona melhor para coisas como dados buscados e modelos modificados localmente.

Um desenvolvedor React lendo a documentação e o tutorial já tem essa abstração de estado efêmera à sua disposição, então é possível que isso já esteja em mente ao considerar onde o Redux faz sentido para um projeto.

No entanto, da perspectiva de um desenvolvedor com pouca ou nenhuma experiência com React que deseja adotar uma abordagem funcional para a IU, pode não ser óbvio qual estado pertence ao Redux. Eu fiz vários projetos agora onde a princípio Redux e virtual-dom eram suficientes, mas conforme o aplicativo crescia em complexidade, essa outra "abstração de estado efêmera" se tornou necessária. Isso nem sempre é aparente na primeira vez que você adiciona um redutor de animação com alguns sinalizadores básicos de animação, mas mais tarde se torna uma dor de cabeça.

Pode ser bom para a documentação mencionar qual estado é o correto para o Redux e qual estado é melhor resolvido por outras ferramentas. Pode ser redundante para desenvolvedores React, mas pode ser muito benéfico para outros desenvolvedores terem em mente ao olhar para o Redux e planejar sua arquitetura de aplicativo.

EDIT: o título da edição é "Quais são as desvantagens de armazenar todo o seu estado em um único átomo imutável?" Esse "todo o seu estado em um único átomo imutável" é exatamente o tipo de expressão que acaba obtendo muitos estados errados na árvore Redux. A documentação pode fornecer alguns exemplos explícitos para ajudar os desenvolvedores a evitar esse tipo de armadilha.

@gaearon Tivemos uma conversa interessante no Cycle.js sobre as diferenças arquitetônicas entre um único estado de átomo e pipping. AQUI .

Eu sei que isso não está 100% relacionado ao Redux, mas queria dar uma perspectiva diferente de uma implementação que usa streams Observable como um fluxo de dados em torno de um aplicativo (para Cycle, o aplicativo é um conjunto de funções puras).

Como tudo no Cycle é um Observável, eu estava tendo dificuldades para obter o estado para passar de uma mudança de rota para outra. Isso se deve ao fato de o estado Observável não ter um assinante uma vez que uma rota foi alterada e pensar por que não implementar um único estado de átomo, então, a qualquer momento alterado no meu aplicativo, ele reportaria de volta ao armazenamento de nível superior, ignorando qualquer tubulação ( ver terrível desenhos aqui ).

Com o Cycle, porque os efeitos colaterais acontecem em Drivers, você geralmente tem que fazer aquele loop do driver de nível superior para o componente e vice-versa, então eu queria apenas pular isso e voltar direto para a escuta dos componentes. Estava tomando Redux como fonte de inspiração.

Não é o caso de estar certo ou errado, mas agora eu faço piping e descobrimos um driver de estado, ter a capacidade de canalizar meu estado para componentes diferentes conforme preciso é realmente flexível para refatoração, prototipagem e forte compreensão cada fonte de estado, cada consumidor e como eles chegaram uns aos outros.

Além disso, os novatos no aplicativo (se eles entenderem o Cycle, é claro) podem facilmente conectar os pontos e muito rapidamente desenhar uma representação visual de como o estado é tratado e canalizado e tudo isso a partir do código.

Desculpe antecipadamente se isso está totalmente fora de contexto, queria demonstrar como um paradigma diferente teve uma conversa semelhante: smiley:

@timdorr

Cada widget precisará manter seu próprio estado sem sobreposição entre os dois. Uma equipe de produto é provavelmente atribuída a cada widget, portanto, eles podem não saber os efeitos do código de outras pessoas no ambiente.

Eu acho que é melhor quando o widget tem seu próprio estado (talvez até mesmo sua própria loja (redux?)) Para que possa funcionar de forma independente em qualquer aplicativo (mashup-) e receber apenas algumas das propriedades dele. Pense no widget do tempo. Ele pode buscar e mostrar dados por si mesmo e receber apenas propriedades como cidade, altura e largura. Outro exemplo é o widget do Twitter.

Ótima discussão!

Principalmente, acho inconveniente combinar vários aplicativos / componentes / plug-ins.

Não posso simplesmente pegar um módulo que criei e jogá-lo em outro módulo / aplicativo, pois precisarei importar separadamente seus redutores para o armazenamento do aplicativo e terei que colocá-lo em uma chave específica que meu módulo conhece. Isso também me limita a duplicar o módulo se eu usar @connect , porque ele se conecta a todo o estado.

Por exemplo:

Estou construindo um messenger que se parece com o iMessage. Ele tem um estado redux de currentConversationId etc. Meu componente Messenger tem @connect(state => ({ currentConversationId: state.messenger.currentConversationId })) .

Quero incluir este Messenger em um novo aplicativo. Terei de import { rootReducer as messengerRootReducer } from 'my-messenger' e adicioná-lo a combineReducers({ messenger: messengerRootReducer }) para que funcione.

Agora, se eu quiser ter duas instâncias de <Messenger> no aplicativo com dados diferentes, não posso, porque estou vinculado a state.messenger no componente Messenger. Fazertrabalhar em uma fatia específica da loja me fará usar um @connect .

Além disso, digamos que o aplicativo que o contém tenha um certo nome de ação existente no módulo my-messenger , haverá uma colisão. É por isso que a maioria dos plug-ins redux tem prefixos como @@router/UPDATE_LOCATION .

Algumas ideias aleatórias / malucas:

1

@connect pode se conectar a uma fatia específica da loja, então posso incluir em meu aplicativo vários <Messenger> s que se conectam a sua própria fatia de dados, como state.messenger[0] / state.messenger[1] . Deve ser transparente para <Messenger> que ainda continua se conectando a state.messenger , no entanto, o aplicativo que o contém pode fornecer a fatia por algo como:

@connect(state => ({ messengers: state.messengers }))
class App extends Component {
  render() {
    return (
      <div>
        {this.props.messengers.map(messenger =>
          <ProvideSlice slice={{messenger: messenger}}><Messenger /></ProvideSlice>
        }
      </div>
    )
  }
}

Há um problema ao usar entidades normalizadas globais, state.entities também precisará estar presente, portanto, junto com o estado fatiado, o estado inteiro precisa estar presente de alguma forma. ProvideSlice pode definir algum contexto que Messenger de @connect será capaz de ler.

Outro problema é como vincular ações disparadas por componentes em <Messenger> afetando apenas aquela fatia. Talvez @connect sob ProvideSlice execute mapDispatchToProps ações apenas nessa parte do estado.

2

Combine as definições de store e reducer , de modo que cada redutor também possa ser autônomo. Store soa como um controlador enquanto um redutor é uma visualização. Se adotarmos a abordagem da react, "tudo é um componente" (ao contrário do controlador / diretiva do angular 1.x), isso pode permitir que os componentes e seus estados / ações sejam realmente independentes. Para coisas como entidades normalizadas (ou seja, 'estado global'), algo como o contexto do React pode ser usado, e pode ser acessível por meio de todas as ações (por exemplo, getState em thunk ) / componentes conectados.

@elado A ideia mais inteligente que tive por agora é arquitetar com "provedores" em mente: https://medium.com/@timbur/react -automatic-redux-supplies-and-replicators-c4e35a39f1

Thx @sompylasar. Estou fazendo uma pequena viagem ao caixa eletrônico, mas quando tiver a chance, planejo escrever outro artigo descrevendo provedores de forma mais sucinta para aqueles que já estão familiarizados com React e Redux. Qualquer pessoa já familiarizada deve achar que é muito simples / fácil. :)

@gaearon ainda gostaria de ouvir sua opinião sobre isso.

Minha maior implicância é que é mais difícil compor, reutilizar, aninhar e geralmente mover os componentes do contêiner porque existem duas hierarquias independentes ao mesmo tempo (visualizações e redutores). Também não está totalmente claro como escrever componentes reutilizáveis ​​que usam Redux como detalhe de implementação ou desejam fornecer uma interface amigável ao Redux. (Existem diferentes abordagens.) Também não estou impressionado com cada ação tendo que ir “todo o caminho” para cima em vez de entrar em curto-circuito em algum lugar. Em outras palavras, gostaria de ver algo como o modelo de estado local React, mas apoiado por redutores, e gostaria que fosse altamente prático e ajustado para casos de uso reais, em vez de uma bela abstração.

@gaearon alguma ideia sobre como começar a implementar esse sistema?

@gaearon

Em outras palavras, gostaria de ver algo como o modelo de estado local React, mas apoiado por redutores

Acho que a parte complicada é que o estado local do React está vinculado ao ciclo de vida de um componente, então, se você tentar modelar o estado local no Redux, terá uma conta para "montagem" e "desmontagem". Elm não tem esse problema porque 1) os componentes não têm ciclos de vida e 2) a "montagem" de uma visualização é derivada do modelo, enquanto no React, o estado local só existe _após_ uma visualização ser montada. (editar: mais preciso dizer logo antes de ser montado, mas depois que a decisão de montar já foi tomada)

Mas, como você alude, a arquitetura Elm - indo "até o fim" para cima - tem suas próprias desvantagens e não se encaixa muito bem com o ciclo de atualização e reconciliação do React (por exemplo, requer o uso liberal de shouldComponentUpdate() otimizações ... que quebram se você ingenuamente "encaminhar" um método de envio dentro de render() .)

A certa altura, sinto vontade de usar redutores + setState e encerrar o dia: D A menos / até que o React descubra uma história para externalizar a árvore de estado ... embora talvez seja isso que estamos realmente discutindo aqui

Acho que é isso que @threepointone está tentando resolver com https://github.com/threepointone/redux-react-local

@acdlite o modelo conceitual do Elm funciona muito bem para o estado local, mas realmente parece uma dor de usar em aplicativos do mundo real onde temos que usar bibliotecas, dar foco na montagem, lidar com efeitos de paralaxe ou qualquer outra coisa ... Veja https: // github.com/evancz/elm-architecture-tutorial/issues/49

@slorber Sim, eu concordo. Não foi essencialmente isso o que acabei de dizer? : D

Suponho que suas preocupações sejam ligeiramente diferentes. _Se_ (grande "se") você usasse a arquitetura Elm no React, entretanto, não acho que você teria os problemas que mencionou porque ainda teríamos acesso aos ciclos de vida, setState como uma saída de emergência, etc.

Sim, estamos na mesma página @acdlite
A arquitetura do Elm com React resolveria esse problema.
Até mesmo Elm poderia no futuro, pois poderia permitir o uso de ganchos Vdom.

No entanto, a arquitetura Elm requer alguns clichês. Eu acho que não é realmente sustentável a menos que você use Flow ou Typescript para as ações de empacotamento / desdobramento, e seria melhor ter uma maneira mais simples de lidar com esse estado local, como a solução de @threepointone

Tenho outra preocupação com a arquitetura do Elm é que a ação aninhada funciona muito bem para o estado do componente local, mas acho que não é uma boa ideia, uma vez que você precisa de comunicação entre os componentes desacoplados. Se widget1 tiver que desembrulhar e examinar ações profundamente aninhadas para encontrar algum evento disparado por widget2, há um problema de acoplamento. Expressei algumas idéias aqui e acho que usar o Elm com 2 "caixas de correio" pode resolver o problema. É também nisso que o redux-saga pode ajudar em arquiteturas que não sejam de olmos com eventos planos.

Começo a ter uma boa ideia sobre como arquitetar aplicativos redux escaláveis ​​e vou tentar escrever algo sobre isso quando estiver menos ocupado

@gaearon Você está falando sobre react-redux-provide em sua última postagem aqui? Estou perguntando porque se você está, com base em sua resposta, está claro que você não deu uma olhada de perto.

Além disso, com relação à menção de externalização da árvore de estados , esse é exatamente o problema que os provedores foram projetados para resolver.

A API fornecida pelo decorador provide é certamente uma abstração e sim, atualmente depende de redux , mas pensar nela como dependente de redux é a maneira errada de pense nisso, já que na verdade é apenas uma "cola" entre os componentes e a árvore de estados, já que o context do React ainda não está totalmente desenvolvido. Pense nisso como um método ideal de manipulação e representação de árvore (s) de estado via actions e reducers (ou seja, o estado da loja) como props , onde você pode para essencialmente declarar actions e reducers como propTypes (e / ou contextTypes no futuro) semelhante a como você import qualquer outra coisa dentro do seu aplicativo. Quando você faz isso, tudo se torna insanamente fácil de raciocinar. context do React poderia potencialmente evoluir para incluir esses recursos básicos e, nesse caso, levaria 2 segundos inteiros para executar o grep e remover @provide e import provide from 'react-redux-provide' para que você então saiu com componentes simples que não dependem mais dele. E se isso nunca acontecer, não é grande coisa, porque tudo continuaria a funcionar perfeita e previsivelmente.

Acho que você está simplificando demais o problema. Você ainda precisa resolver a montagem e desmontagem.

@acdlite Tem um exemplo?

Se uma das coisas a que você está se referindo é (por exemplo) recuperar props de algum banco de dados com base em algum id ou qualquer outra coisa, isto (entre quase qualquer outra aplicação prática que você possa imaginar) pode ser facilmente alcançado com os fornecedores. Depois de terminar algumas outras coisas nas quais estou trabalhando, ficarei feliz em mostrar como é fácil. :)

Pode ser interessante: implementações de estado reativo comparadas .
Elm, Redux, CycleJS ... (trabalho em andamento)

Outra possível necessidade de armazenamento múltiplo é quando várias _linhas de tempo_, _viagem de tempo_, salvando locais e salvando frequência são possíveis e necessários. Por exemplo, user data e user actions que são específicos para um único usuário e common data e group actions específicos para um grupo de usuários (por exemplo, projeto multiusuário compartilhado ou documento). Isso torna mais fácil separar o que pode ser desfeito (alterações nos dados pessoais) do que é corrigido (edições já aumentadas por outros usuários) e também facilita ter diferentes ciclos de salvamento e / ou sincronização.

Estamos na fase de design / desenvolvimento inicial de um aplicativo angular 1.x com a mentalidade de desenvolvimento angular 2.0 e pensando em usar redux. No passado, usamos o fluxo com vários armazenamentos respondendo a alguns despachos comuns e a alguns despachos dedicados.

Redux me parece ótimo, mas não consigo entender o conceito de loja única para todo o aplicativo. Como outros também apontaram acima, temos uma série de widgets grandes que deveriam ser hospedados em diferentes aplicativos de consumo e precisavam de armazenamentos de dados dedicados para cada um deles.

Mesmo que os redutores possam ser divididos e possam conviver com o recurso / widget que os utiliza, temos que agrupar todos eles na hora de criar a loja. Gostaríamos de ver nossos widgets existindo sem ter qualquer referência direta / indireta a redutores que não importam.

Estamos realmente ansiosos por alguma orientação na forma de documentação ou um comentário para descrever as melhores práticas em tais situações antes de começar a usar o redux em nosso aplicativo. Qualquer ajuda será realmente apreciada. Obrigado.

@VivekPMenon : quais são suas preocupações específicas? Tamanho da árvore de estado? Ser capaz de atribuir nomes a ações / manter redutores isolados? Capacidade de adicionar ou remover redutores dinamicamente?

Pelo que vale a pena, acabei de completar uma lista de links catalogando o ecossistema do addon Redux. Há uma série de bibliotecas que tentam abordar alguns desses conceitos de isolamento / duplicação / escopo. Você pode querer dar uma olhada na página do página local / estado do componente para ver se alguma dessas bibliotecas pode ajudar em seus casos de uso.

@markerikson. Muito obrigado pela resposta. Minha confusão é principalmente em torno do segundo ponto (isolamento). Suponha que uma de nossas equipes crie o widget A que é baseado em fluxo e precisa usar o armazenamento para gerenciar seu estado. Suponha que eles irão criar um pacote jspm / npm se e distribuí-lo. O mesmo é o caso com outra equipe que cria o widget B. Esses são widgets / pacotes independentes e não precisam ter dependência direta / indireta um do outro.

Neste contexto, no mundo de fluxo regular. Os widgets A e B têm seus próprios armazenamentos em seu pacote (eles podem ouvir algumas ações comuns, mas presumem que têm mais ações que não são comuns). E ambos os widgets dependerão do dispatcher global. No padrão redux, onde essa loja seria criada (fisicamente). Os componentes de IU do widget A e B precisam usar a instância da loja para ouvir as mudanças e obter o estado. Em fluxo regular, eles operam em sua própria instância de armazenamento. Estou tentando visualizar como o conceito de loja única vai lidar com essa situação.

@VivekPMenon esta é uma questão válida que alguns de nós estamos tentando resolver.

Você pode obter alguns insights aqui: https://github.com/slorber/scalable-frontend-with-elm-or-redux

Além disso, dê uma olhada na minha resposta sobre a saga Redux aqui: http://stackoverflow.com/a/34623840/82609

@slorber Muito obrigado pela orientação. Ansioso pelas ideias que surgem aqui https://github.com/slorber/scalable-frontend-with-elm-or-redux

@taggartbg Você acabou de abrir um problema? Estou interessado em algo semelhante - ao ter um roteador react com diferentes subárvores independentes, acho problemático manter o estado da rota anterior enquanto navego para um novo ramo.

@slorber

Deixe-me começar dizendo que a saga redux é um projeto maravilhoso com muito pensamento colocado nele e isso de forma alguma tenta diminuir isso.

Mas eu não o usaria em nenhum dos meus projetos em seu estado atual. Aqui estão algumas razões do porquê

Sagas não têm estado

Você não pode serializar o estado de um gerador, eles fazem sentido enquanto estão funcionando, mas estão completamente ligados ao estado da saga. Isso causa uma série de limitações:

  • Você não pode viajar no tempo, geradores
  • Você não pode traçar o caminho que levou ao estado atual de uma saga
  • Você não pode definir um estado inicial
  • Você não pode separar a lógica de negócios do estado do aplicativo [*]
  • Você não pode armazenar sagas

[*] Isso poderia ser interpretado como a definição da lógica de negócios dentro da definição do modelo do aplicativo, tornando o M e o C no MVC a mesma coisa, o que poderia ser considerado um antipadrão

O despacho de ações não é determinístico

Uma das grandes vantagens de usar redux é manter um entendimento simples de como o estado reage a uma ação. Mas a saga redux torna as ações sujeitas a resultados inesperados, pois você não pode dizer de que parte da saga está começando.

Estado serializável

O fato é que o estado atual da saga tem um impacto sobre o estado atual do aplicativo, e as pessoas se estressam em manter o estado serializável, mas como traduzir isso para sagas? Se você não pode serializar totalmente o estado, então você não está _realmente_ o serializando. Você basicamente não pode recuperar o estado de um aplicativo sem recuperar a saga também.


Armazenar lógica no estado é ruim e, em minha opinião, é isso que a saga redux faz de certa forma.

É perfeitamente normal supor que o estado do seu aplicativo é específico para _seu_ aplicativo. Isso significa que também não há problema em assumir que o estado do aplicativo é previsível, desde que opere sobre _seu_ aplicativo. Isso significa que não há problema em manter as informações do armazenamento que determinam como seu aplicativo se comporta sem realmente salvar a lógica lá.

@eloytoro

Eu entendo suas preocupações. Se você olhar para os problemas da saga redux, verá que essas coisas são discutidas e que há soluções a serem encontradas.

Observe também que o redux-saga opta por implementar com geradores, mas não é um requisito para implementar o padrão saga, e há prós e contras.

Existe um efeito select que permite que você use o estado redux dentro de sua saga redux, então se você não quiser esconder o estado dentro de geradores, você pode simplesmente colocar seu estado de saga dentro de redux-store diretamente, mas isso envolve mais trabalho.

Existem devtools de saga que permitem rastrear a execução de sagas.

Existem soluções possíveis para a viagem no tempo, mas nenhuma implementação concreta ainda

Direito. Um tópico bastante longo e muito interessante. Estive comparando diferentes abordagens de gerenciamento de estado.

Estou mantendo algumas coisas em mente ao comparar:

  1. única fonte de verdade
  2. quão fácil / difícil é renderizar no lado do servidor e alimentar os dados iniciais no lado do cliente
  3. devtools como desfazer / refazer e armazenamento de histórico persistente após recarregamentos.
  4. animações, animações, animações. Eu sempre me pergunto _ "como as animações funcionariam com a solução a ou b" _. Esta é uma chave para mim, pois os produtos que estou desenvolvendo são centrados na UX. Eu compartilho muitas das confusões que @jsonnull tem.
  5. reutilização de componentes dentro de outro aplicativo
  6. hidratação dos dados referenciados (_major pain_) a fim de manter a integridade (relacionada à alimentação de dados iniciais do lado do servidor ou qualquer outra fonte de dados)
  7. comunidade de outros desenvolvedores para discussão como esta

Portanto, esses são os pontos que desconfio quando penso em gerenciamento de estado. Redux é o vencedor ao marcar a maioria das caixas. Eu conheço o Redux muito bem e foi a primeira coisa que me fez respirar com calma no reino dos clientes do lado js.

Os outros projetos mais notáveis ​​que estive analisando são o padrão SAM (_difícil de entrar na mentalidade e nenhuma ferramenta_) e Mobx (_conhecido por seu armazenamento mutável_).

Ambos compartilham a ideia de fonte única da verdade e acho interessante ver como eles lidam com isso.

Minha pesquisa (limitada, é claro) sobre Mobx está resultando em:

  1. Mobx (assim como Redux) está defendendo uma única fonte de verdade, mas como uma classe com propriedades observáveis. Este conceito cria as diferenças com redux e fluxo como um todo:

    1. Mobx também defende sub-lojas (_nested / parte do único global_). Para mim, parece muito com definir um esquema do lado do cliente. É um pouco irritante, pois provavelmente você também tem um esquema no servidor. Vejo semelhanças com redux-orm . AFAIK, os sub-armazenamentos devem ser passados ​​para componentes como props (desta forma, o componente não depende do mapeamento de estado para props ou certa chave da árvore global). No entanto, passar uma única propriedade quebra minha ideia de "propriedades" - elas devem ser descritivas do que o componente precisa. Isso realmente me faz pensar se isso seria um problema se React propTypes pudesse ser usado para definir a forma da loja que está sendo passada. Além disso: ter store e sub-store como classes torna possível referenciar os dados diretamente sem a necessidade de normalização, mas isso é meio inútil se você deseja alimentar dados iniciais por causa de:

    2. A ideia de armazenar como classe, entretanto, significa que o desenvolvedor não tem serialização pronta para uso, o que significa que é seu trabalho se você deseja alimentar os dados iniciais.

  2. Histórico e desfazer / refazer podem ser obtidos por meio de instantâneos do estado após sua alteração. Mas você mesmo precisa escrever serializador, desserializador. Um exemplo é dado aqui: https://github.com/mobxjs/mobx-reactive2015-demo/blob/master/src/stores/time.js
  3. O bom de escrever serializadores é que você pode deixar coisas de fora. parece um bom lugar para armazenar dados relacionados a animações não persistentes. Portanto, se você serializá-lo, simplesmente o ignora e não tem um refazer / desfazer cheio de etapas de animação.
  4. Mobx defende manter as derivações de dados na loja (como métodos da loja) em oposição ao decorador reselect + connect . Isso parece meio errado para mim.
  5. Devido à sua natureza observável, mobx torna fácil definir componentes puros eficientes. A comparação superficial de propriedades não é um problema porque o conteúdo é observável de qualquer maneira.

Comparando SAM e Redux, gosto de pensar sobre como a lógica flui:

  1. Restaurado:

    1. Definir uma loja

    2. Definir funções que lidam com mudanças na loja (redutores)

    3. Defina identificadores de evento (ação de redux) (tipo de ação de redux) para cada mudança de loja e execute redutor específico quando corresponder

    4. Mapeie o armazenamento para as propriedades do contêiner de componente, opcionalmente, passando os manipuladores de interação do usuário (clique, digite, sscroll, etc) como funções que simplesmente acionam evento / ação de despacho.

    5. O componente aciona um evento (ação redux) que é comparado a todos os redutores

    6. Renderize com o novo estado.

  2. SAM:

    1. Defina uma loja.

    2. Defina uma função de acessador / configurador para a loja. Este configurador aceita quaisquer dados e, puramente com base nos dados, tenta descobrir onde colocá-los no armazenamento e se precisa solicitar dados do servidor ou mantê-los lá. Descobrir o que fazer com os dados nem sempre é possível apenas inspecionando-os, então às vezes identificadores / sinalizadores são usados ​​(como isForgottenPassword ). Este configurador também se destina a validar os dados e armazenar os erros. Após o setter terminar -> renderizar novamente com o novo estado.

    3. Defina funções que aceitam dados, os transformam (alternativa aos redutores redux) e executam o configurador passando os dados transformados. Essas funções agora tem ideia sobre o formato da loja. Eles só sabem os dados que foram transmitidos.

    4. Mapeie os dados de armazenamento para as propriedades do contêiner do componente, mas também é obrigatório passar manipuladores de manipuladores de interação do usuário (clique, digite, role, etc). Observe que essas são as funções reais (conhecidas como redutores em redux) e não funções que executam uma chamada de despacho.

    5. Obs: não há despacho de ações, passagem direta de redutores.

Então, todos redux, SAM e Mobx mantêm o estado em um só lugar. A maneira como eles fazem isso traz seus próprios contratempos. Nenhum pode bater redux e seu ecossistema até agora.

A maior desvantagem que vejo é também o tratamento do estado não serializável.
No meu caso, tenho uma API de plugin e, portanto, ações e lógica que não tem nada a ver com Redux, mas precisa interagir com meu aplicativo. Então, basicamente, eu preciso recriar inicialmente um grupo de classes com base em um determinado estado.

Estou trabalhando em alguns conceitos de como lidar melhor com esse tipo de estado não serializável. Incluindo recriá-lo a partir de qualquer estado. Já consegui mover tudo, exceto referências de função / objeto de volta para a loja. O que resta são objetos com seus próprios ciclos de vida com uma API definida que interagem com meu aplicativo e, portanto, com minhas ações. - É semelhante aos componentes React, não apenas para renderização da IU, mas para lógica customizada.

Não sei se vocês leram este artigo de Erik Meijer . Apenas uma citação simples:

Ao contrário da crença popular, tornar as variáveis ​​de estado imutáveis ​​não chega nem perto de eliminar os efeitos imperativos implícitos inaceitáveis

Talvez seja hora de revisitar todo esse absurdo de imutabilidade. Todo mundo vai dizer que é um problema de desempenho quando a árvore de estado único cresce em tamanho. O padrão SAM torna a mutação um cidadão de classe primária do modelo de programação. Muitas simplificações acontecem quando você faz isso, de não precisar mais de Sagas, para todas essas máquinas de estado auxiliares necessárias para manter o controle dos efeitos (por exemplo, busca).

O artigo realmente não argumenta a seu favor. Erik Meijer revisita "esse absurdo da imutabilidade" e o abraça completamente . Ele argumenta que remover a mutação de estado é um primeiro passo básico , fundamental, mas não suficiente. Seu objetivo final é a eliminação completa dos efeitos colaterais implícitos, o que tornaria qualquer gerenciamento de estado obsoleto (não haveria nenhum estado para gerenciar!).

Sou fã do trabalho de Erik Meijer e irei ler e ouvir ansiosamente tudo o que ele tem a dizer, embora também tenha em mente que ele não opera sob as mesmas restrições comerciais, tecnológicas e intelectuais que eu.

o principal problema da engenharia de software é que todos parecem acreditar que uma atribuição é uma mutação. As linguagens de programação suportam apenas atribuições, a mutação é deixada para a interpretação do programador.

Adicionar um wrapper de função a um monte de atribuições não vai se tornar automaticamente a melhor maneira de alterar o estado. Isso é um absurdo.

Há uma teoria por trás da mutação de estado (aplicativo), é chamada de TLA +, curiosamente ... Erik nunca menciona TLA + ...

JJ-

Deixe-me explicar, já que parece haver muita confusão. O artigo de Erik demonstra eloqüentemente que as linguagens de programação imperativas estão um tanto quebradas quando se trata de estado de mutação. Eu acredito que todos concordam com isso.

A questão é como fazer e consertar isso? Você não concorda que esse é o tipo de problema que requer uma abordagem de " primeiro princípio "?

Há praticamente um único princípio com o qual todos podemos concordar na ciência da computação, Dr. Lamport afirma: "Grande parte da ciência da computação trata de máquinas de estado." e ele continua: "Ainda assim, os cientistas da computação estão tão focados nas linguagens usadas para descrever a computação que eles praticamente não sabem que essas linguagens são todas máquinas de estado descritas."

Na verdade, todas as linguagens de programação nos permitem especificar "máquinas de estado", independentemente de sua semântica. Infelizmente, muitas linguagens de programação tendem para "ação" ao invés de "ação de estado". Isso é por um bom motivo, há uma grande classe de problemas que são codificados com muito mais eficiência com um formalismo baseado em ação (em que os estados da máquina de estado podem ser amplamente ignorados). Quando você os ignora, você basicamente afirma que todas as ações são possíveis em qualquer estado, o que todos nós sabemos que geralmente não é verdade.

Você pode rir o quanto quiser sobre o exemplo haha ​​de Erik:
// prints Ha var ha = Ha(); var haha = ha+ha;

mas tudo o que ele afirma é que você está realizando a ação errada em um determinado estado. Nada mais nada menos.

O que a programação funcional traz para a mesa? não muito mesmo. Expressa de forma muito complicada a relação entre realizar uma ação e os resultados que ela pode ter. É como tentar atirar uma flecha (mesmo uma flecha monádica inteligente) em um labirinto gigante e torcer para que você acerte o alvo.

O TLA + traz uma forma muito mais natural de lidar com os efeitos, primeiro porque tem uma noção clara do que é um passo e como a mutação se relaciona com a ação. Você é livre para ignorar o TLA +, mas é um pouco como se você estivesse reclamando que tentou enviar um foguete para a lua supondo que a Terra era plana e que era realmente difícil dirigir a nave. Eu sei que vivemos em um mundo onde as pessoas acreditam que podem dobrar a realidade, contanto que muitas pessoas acreditem no que estão dizendo ... que isso ainda não o levará a lugar nenhum.

Então, novamente, eu ficaria muito, muito, muito curioso para ver o que Erik pensa sobre o TLA +. Eu me conectei com ele no LinkedIn e fiz a pergunta. Nunca tive uma resposta ... Afinal o Dr. Lamport só recebeu um prêmio Turing por isso, provavelmente não vale a pena.

Honestamente, @jdubray , você está no limite do troll neste momento. Você repetiu seus argumentos dezenas de vezes neste tópico, em outras edições e em outros lugares. Você insultou Dan e a comunidade Redux, exibiu suas credenciais e continua invocando as palavras "TLA +" e "Lamport" como se fossem o Santo Graal e a Resposta à Vida, ao Universo e a tudo. Você insistiu que o SAM é muito superior ao Redux, mas sem escrever muito na forma de aplicativos de comparação significativos, apesar dos inúmeros convites para mostrar evidências. Você realmente não vai mudar a opinião de ninguém que ainda não esteja convencido agora. Sinceramente, sugiro que você passe seu tempo fazendo algo mais produtivo.

Vou encerrar o tópico neste momento. Os comentários podem continuar, eu acho, mas definitivamente não há nada acionável aqui.

@markerikson Eu gostaria de continuar esta discussão, mesmo que não vá a lugar nenhum, o tópico me interessa um pouco. Seria melhor limpar seus comentários tóxicos e manter a boa discussão de código aberto.
Além de @antitoxic (sem trocadilhos) deixar bem claro onde a abordagem a que ele se refere é útil ou não, sabemos o quão ruim os outros problemas ficaram por causa de suas intervenções, então não há mais sentido em ouvir

Tudo bem Mark, você pode remover meus comentários, por que estragar uma conversa tão brilhante?

@jdubray, só você o vê como brilhante. Acho que todos sentimos que você tenta nos vender algo sem mais argumentos do que "Lamport ganhou um prêmio Turing, então me escute, estou certo!".

Não digo TLA + ou o que quer que não seja bom, e talvez no futuro alguém faça um trabalho melhor do que você vendendo. É apenas a maneira como você explica que não nos ajuda a entender em nada, e seu tom condescendente permanente não me convida a investir tempo aprendendo sobre TLA +.

O artigo de Erik demonstra eloqüentemente que as linguagens de programação imperativas estão um tanto quebradas quando se trata de estado de mutação. Eu acredito que todos concordam com isso.

Na verdade, você sempre usa termos como I believe everyone agrees on that em todas as suas postagens. Isso não é algo bom, útil ou acolhedor para as pessoas. Nem todos concordamos com você por padrão, e a maioria de nós não concordou e não quer ler o artigo de Erik. Se você não consegue convencer as pessoas de uma forma acolhedora, sem pedir-lhes que leiam toneladas de papel antes, e fazê-los sentir-se idiotas se não o fizerem ou discordarem de você, não ajudará a espalhar suas idéias.

Você é livre para ignorar o TLA +, mas é um pouco como se você estivesse reclamando que tentou enviar um foguete para a lua supondo que a Terra era plana e que era realmente difícil dirigir a nave.

Isso definitivamente parece uma analogia e certamente não um primeiro princípio . Sério, você acha que esta afirmação deve ser considerada como uma "discussão brilhante"?

Eu acho que as discussões ficam mais brilhantes quando você não fala nelas, porque você monopoliza toda a atenção no TLA + e na verdade impede que pessoas não interessadas no TLA + tentem mandar seu foguete para a lua com algo mais concreto, que a média as pessoas podem entender, como Redux.

Ainda não estou dizendo que TLA + não vale a pena, só você está falando sobre isso da maneira errada, para as pessoas erradas. Talvez se Erik não te respondeu, é porque ele também não consegue te entender? Talvez você seja tão inteligente que apenas Lamport pode entendê-lo? Talvez no futuro você ganhe um prêmio Turing? Não sei, mas uma coisa certa é que a discussão atual não traz nada para a mesa.

Talvez faça como @gaearon e crie tutoriais em vídeo TLA + / SAM para egghead. Se você puder explicar suas ideias de uma forma muito simples, em menos de 2h de vídeos, e no final seus padrões clicarem e parecerem úteis para os desenvolvedores, você ganha. Redux fez isso para muitas pessoas. Você apenas "tentou" explicar teoricamente por que SAM / TLA + é melhor que Redux, e forneceu uma implementação TodoMVC de aparência horrível. Somos pesquisadores de ciência da computação aqui, precisamos de material concreto. Você pode nos mostrar um aplicativo, escrito em Redux em vez de escrito em SAM, onde as pessoas realmente sentiriam que a implementação do SAM é melhor? Esse não foi o caso até agora.

@Niondir Eu descobri uma maneira de usar sagas nos casos em que você precisa recuperar um estado.
É mais como um padrão ou um conjunto de regras a seguir

  • Separe suas sagas para que cada uma delas não tenha mais do que um único put . Se você tiver sagas com mais de put nelas, divida-as e acorrente-as com um efeito call . Por exemplo
function* mySaga() {
  yield put(action1());
  yield put(action2());
}

// split into

function* mySaga() {
  yield put(action1());
  yield call(myNextSaga);
}

function* myNextSaga() {
  yield put(action2());
}
  • Sagas com efeitos de take devem ser divididas na lógica que segue take e o ponto de início da saga. Por exemplo
function* mySaga() {
  yield take(ACTION);
  // logic
}

// split it into

function* rootSaga() {
  yield takeLatest(ACTION, mySaga);
}

function* mySaga() {
  // logic
}
  • Para cada saga, crie uma espécie de "ponto de verificação", o que significa que no init leia a partir do estado e de lá call a saga de que você precisa
function* rootSaga() {
  // emit all forks and take effects that need to run in the background
  yield call(recoverCheckpoint);
}

function* recoverCheckpoint() {
  const state = yield select();
  if (state.isFetching) {
    // run the saga that left the state like this
  }
}

A explicação de por que isso funcionaria é baseada em um conjunto de suposições que geralmente são verdadeiras

  • take efeitos precisam de ações para serem despachados para a loja, é seguro sempre chamar essas sagas no init, mesmo se você estiver se recuperando, porque elas permanecem ociosas até que o usuário interaja com a página e / ou as sagas já estejam em execução
  • Não permitir mais de uma chamada put por saga permite mudanças atômicas no estado, da mesma forma que uma ação só pode alterar o estado de uma única maneira, uma saga é limitada a essa capacidade também, o que meio que cria uma correlação entre sagas e despacho de ação.

Dividir sagas dessa forma também tem alguns _efeitos_ positivos

  • Sagas são mais reutilizáveis
  • Sagas são alteradas mais facilmente
  • Sagas são mais fáceis de entender

mas @slorber :

Muitas pessoas acham este exemplo de "jogo de pesca" bastante interessante de codificar, pois ilustra em primeira mão como o SAM funciona em sua totalidade, incluindo uma representação de estado dinâmico e o "predicado de próxima ação". NAP alivia a necessidade de Sagas e não requer a quebra do princípio Redux nº 1, que é uma árvore de estado único, mas o que eu sei?

Às vezes, quando estou usando o Ableton Live, o pensamento surge na minha cabeça "seria possível escrever a GUI do Ableton Live como um aplicativo React / Redux?" Acho que a resposta é não, seria muito difícil obter um desempenho aceitável. Há um motivo pelo qual ele foi escrito em Qt e um motivo pelo qual o Qt tem uma arquitetura de sinais / slots.

Tenho usado React / Redux com Meteor. Eu armazeno documentos mongo no estado Immutable.js despachando ações Redux para os callbacks para Mongo.Collection.observeChanges . Recentemente descobri que, ao assinar vários milhares de documentos, a IU ficava extremamente lenta na inicialização porque milhares de ações Redux estavam sendo despachadas enquanto o Meteor enviava os resultados da assinatura inicial um por um, causando milhares de operações Immutable.js e milhares de rerenders tão rápido que possível. Presumo que RethinkDB também funcione dessa maneira, embora não tenha certeza.

Minha solução foi criar um middleware especial que colocaria de lado essas ações de coleta e depois as despacharia em lotes em uma taxa acelerada, para que eu pudesse adicionar cerca de 800 documentos em uma única mudança de estado. Isso resolveu os problemas de desempenho, mas alterar a ordem do evento é inerentemente arriscado porque pode levar a um estado inconsistente. Por exemplo, eu tive que garantir que as ações de status de assinatura fossem despachadas por meio do mesmo regulador, para que as assinaturas não fossem marcadas como prontas antes de todos os documentos terem sido adicionados ao estado Redux.

@ jedwards1211 Tive um problema semelhante (muitas mensagens pequenas que comporiam os dados iniciais).

Resolvido da mesma maneira, fazendo uma atualização em lote. Em vez de um middleware, usei um (pequeno) mecanismo de fila personalizado (envia todas as atualizações em uma matriz e, em intervalos regulares, se houver algo para atualizar, atualize todas ao mesmo tempo.

@andreieftimie sim, é mais complicado no meu aplicativo porque há algumas interações da interface do usuário que exigem atualizações imediatas, como arrastar e aplicar zoom nos gráficos. Portanto, preciso ter uma camada de middleware para decidir se devo despachar uma ação imediatamente ou colocá-la em uma fila para despacho em lote.

Para valer a pena, tive a ideia de criar "branches". Isso desafia totalmente o princípio de loja única, mas parecia um bom compromisso.

https://github.com/stephenbunch/redux-branch

@ jedwards1211 Acho que, à medida que os aplicativos aumentam de complexidade, eventualmente é necessário separar certos tipos de dados e certos tipos de atualizações para que certos armazenamentos não lidem com a carga total.

Digamos que haja algum estado principal que você deseja sincronizar com o back-end, algum estado de animação para que os componentes de IU do seu aplicativo sejam exibidos corretamente de acordo com as ações atuais sendo realizadas e talvez você tenha alguns dados de depuração que está rastreando separadamente.

Neste exemplo inventado, certamente é possível construir tudo isso a partir de uma única loja Redux, mas dependendo de como você a arquitetou, haverá algumas linhas borradas entre as preocupações de vários ramos da árvore de estado e talvez falta de clareza em relação a qual estado uma determinada ação deve interagir.

Eu acho que é perfeitamente razoável usar diferentes estratégias de gerenciamento de estado de acordo com como você deseja que o estado tenha o escopo e quais características de desempenho você espera dele. Redux é adequadamente voltado para um estado no qual você pode querer reverter ou repetir ações, ou serializar e retornar mais tarde. Para o estado de animação, você pode usar um armazenamento mutável ou reativo se quiser - haverá menos sobrecarga dessa forma e você não poluirá o estado do aplicativo com este estado de interação transitório (mas ainda necessário).

Muito rápido, quero enfatizar o uso da próxima arquitetura de fibra React. Aqui está um link , desculpas se meu entendimento está um pouco desatualizado. A grosso modo, a arquitetura de fibra reconhece que existem diferentes tipos de atualizações que se propagam por meio de uma árvore de componentes React, e há diferentes expectativas em relação a quando e como essas atualizações serão executadas. Você deseja que as atualizações de animação sejam rápidas, responsivas e confiáveis ​​e não deseja que grandes atualizações de interação introduzam falhas em suas animações e coisas assim.

Portanto, a arquitetura de fibra divide as atualizações em pacotes de trabalho e os programa de acordo com uma prioridade com base no trabalho que está sendo executado. As animações têm alta prioridade - para que não sejam interrompidas, e atualizações mecânicas mais lentas têm prioridade mais baixa.

Pulei muitos detalhes sobre as fibras React e provavelmente entendi algumas coisas erradas no processo, mas meu ponto é que esse é o tipo de abordagem granular que acredito ser necessária para seu armazenamento de dados com diferentes tipos de dados.

Se eu estivesse construindo um aplicativo complexo hoje, começaria com uma classe de loja de nível superior semelhante ao Redux, apoiada por algumas lojas diferentes. Aproximadamente:

  • O armazenamento de dados de nível superior tem uma fila para as ações recebidas
  • As ações são para ações de animação que devem ser despachadas rapidamente ou interações de aplicativos que têm prioridade mais baixa
  • As ações de animação na fila são despachadas para um back-end observável a partir de um loop requestAnimationFrame
  • As ações de interação são despachadas para redux quando as ações de animação da fila são concluídas

Esta história parece muito próxima de uma solução de estado completa baseada no Redux. Redux é adequado para dados que você deseja visualizar após uma sequência de ações, e há muitos outros estados por aí que também precisam de tratamento cuidadoso.

@jsonnull Nunca precisei armazenar o estado da animação em uma loja - o estado local sempre foi ideal para meus casos de uso de animação. Eu ficaria curioso para saber se há alguns casos de uso para os quais o estado de animação local é completamente inadequado. Parece que eles têm ótimas ideias para a nova arquitetura.

@ jedwards1211 Há alguns casos em que o estado de animação local não funciona. Admito que não são casos com os quais você se depara com todos os aplicativos, mas acho que aparecem com frequência suficiente.

Em um caso, você está usando uma biblioteca diferente da Redux, onde você não tem o estado local. (Ok, sim, eu sei que estou trapaceando um pouco aqui.) Se você estiver usando uma abordagem de hiperescrito muito enxuta, sem virtual dom, componentes funcionais puros, então, em vez do estado local, você terá que passar alguma animação estado para o nó raiz ou qualquer nó que você está re-renderizando.

Nesse caso, ter uma árvore de estado com alguns detalhes de animação configurados permitirá que você contorne a falta de estado local para que possa acionar animações, executá-las por um período e muito mais.

O principal aqui é que há duas maneiras de fazer essas animações - fazê-las como parte de sua renderização e manter a metáfora "IU como uma função de estado" intacta ou alterar o DOM separadamente. Quase sempre a melhor resposta é apenas renderizar novamente em vez de sofrer mutação.


Agora, um exemplo em que você tem a capacidade de manter alguma animação no estado local - construindo um jogo de navegador com uma IU baseada em React em cima dele.

  • Normalmente, você conduzirá o jogo usando tiques ... o tempo do jogo começa em 0 e é incrementado a cada quadro. As animações principais do jogo podem ser feitas na tela ou em WebGL, onde não há estado local, então você vai basear o tempo de início e o progresso da animação com base nesses ticks.

Ex: um personagem sprite tem uma animação composta por 10 quadros, e você deseja reproduzir a animação em aproximadamente 400ms, então você vai mudar o sprite desenhado a cada 2 ticks ou mais.

Seus carrapatos também podem ser carimbos de data / hora, se desejar uma resolução mais alta.

  • Seu jogo também pode pausar, caso em que você pode querer interromper algumas animações feitas no lado da IU do React.

Neste exemplo de jogo, o que você não quer fazer é incrementar seus ticks como parte de uma ação tick ... em vez disso, você deseja incrementar ticks separadamente, possivelmente em uma estrutura observável. E quando você despacha ações Redux, como movimento de personagem do teclado ou ataque, você vai passar o tique atual ou a hora atual para eles, para que possa registrar em qual tique uma ação ocorreu e seus componentes de IU podem registrar localmente a que horas uma animação começou .

Agora você separou o estado de animação global do estado de interação, e se quiser fazer um "replay" usando apenas o estado Redux ou por ações de repetição, você pode, e não está sobrecarregado por incrementar tiques como parte de seu estado árvore.

De modo geral, também é muito melhor coordenar animações passando os tempos de início / parada por meio do Redux e permitindo que os componentes mantenham o resto do estado localmente.

Isso não se aplica apenas a jogos. Qualquer sequência estendida de animações pode passar por isso, como se você quisesse fazer uma série de animações de longa duração em um site usando React.

@jsonnull legal, esse é um ótimo exemplo, obrigado por compartilhar isso.

Eu diria simplesmente - no Redux você simplesmente não pode armazenar objetos de estado com links entre si. Por exemplo - o usuário pode ter muitas postagens e as postagens podem ter muitos comentários e eu quero armazenar esses objetos de uma forma:

var user = {id: 'user1', posts: [], comments: []}
var post = {id: 'post1', user: user, comments: []}
user.posts.push(post);
var comment = {id: 'comment1', post: post, user: user}
post.coments.push(comment)
user.comments.push(comment)
appState.user = user

Redux não permite o uso de hierarquias de objetos com referências circulares. No Mobx (ou Cellx), você pode simplesmente ter referências um-para-muitos e muitos-para-muitos com objetos e simplificou a lógica de negócios muitas vezes

@bgnorlov Eu não acho que nada no pacote redux proíba referências circulares como você está falando - elas apenas tornam mais difícil clonar seu state em seu redutor. Além disso, pode ser mais fácil fazer alterações no estado com referências circulares se você usar Immutable.js para representar seu estado, embora eu não tenha certeza.

Você também pode modelar relacionamentos em seu estado redux usando chaves estrangeiras, embora eu entenda que isso não seja tão conveniente quanto usar referências de objeto do tipo ORM:

var user = {id: 'user1', postIds: [], commentIds: []}
var post = {id: 'post1', userId: user.id, commentIds: []}
user.postIds.push(post.id);
var comment = {id: 'comment1', postId: post.id, userId: user.id}
post.commentIds.push(comment.id)
user.commentIds.push(comment.id)
appState.userId = user.id
appState.posts = {[post.id]: post}
appState.comments = {[comment.id]: comment}

// then join things like so:
var postsWithComments = _.map(appState.posts, post => ({
  ...post,
  comments: post.commentIds.map(id => appState.comments[id]),
})

@ jedwards1211 para clonar o estado com referências circulares em redux, o redutor deve retornar cada nova cópia de um objeto que foi afetado pelas alterações. Se a referência ao objeto muda, o objeto relacionado também precisa ser uma nova cópia e isso será repetido de forma recursiva e irá gerar um novo estado a cada mudança. Immutable.js não pode armazenar referências circulares.
Com a abordagem de normalização, quando algum manipulador de eventos precisa de alguns dados, ele exige todas as vezes para obter o objeto por seu id do estado global. Por exemplo - eu preciso filtrar irmãos de tarefa em algum lugar no manipulador de eventos (as tarefas podem ter estrutura hierárquica).

var prevTask = dispatch((_, getState)=>getState().tables.tasks[task.parentId]).children.map(childId=>dispatch((_, getState)=>getState().tables.tasks[childId])).filter(task=>...) [0]

ou com seletores

var prevTask = dispatch(getTaskById(task.parentId)).children.map(childId=>dispatch(getTaskById(childId)).filter(task=>...)[0]

e este clichê transforma o código em bagunça, comparando com a versão mobx quando eu posso simplesmente escrever

var prevTask = task.parent.children.filter(task=>...)[0]

@ jedwards1211 , @bgnorlov : FWIW, esta é uma das razões pelas quais gosto do Redux-ORM . Ele permite que você mantenha seu armazenamento Redux normalizado, mas torna mais simples fazer essas atualizações relacionais e pesquisas. Na verdade, esse último exemplo é basicamente idêntico ao Redux-ORM.

Acabei de escrever alguns posts descrevendo os fundamentos do Redux-ORM , bem como os principais conceitos e uso avançado .

@markerikson legal, obrigado pela dica!

@gaearon

Uma coisa a notar é que não pretendemos que Redux seja usado para todos os estados. Tudo o que parece significativo para o aplicativo. Eu diria que as entradas e o estado de animação devem ser tratados pelo React (ou outra abstração de estado efêmera). Redux funciona melhor para coisas como dados buscados e modelos modificados localmente.

Achei este comentário extremamente útil. Eu sei que estou atrasado para a festa, mas talvez seja parte do meu ponto. Mais de um ano desde que esse comentário foi postado e este ainda é o único lugar em que vi essa ideia expressa.

Vindo de um fundo angular e decididamente sem fluxo / redux, é muito difícil formular essa ideia por conta própria. Especialmente quando muitos exemplos ainda criam ações para cada tecla em uma caixa de texto. Gostaria que alguém colocasse essa citação em texto 50px no topo de cada página de documentação do Redux.

@leff concordou. Eu sintetizei exatamente essa ideia algum tempo atrás, mas ela não foi divulgada o suficiente. Mesmo se você estiver usando Redux para gerenciamento de histórico, geralmente há muitos estados efêmeros que não precisam sobrecarregar seu gerenciamento de estado no estilo Redux.

Isso não será necessariamente uma surpresa para as pessoas que tiveram a oportunidade de trabalhar, por exemplo, um aplicativo de desktop maduro que faz um rich undo / redo. Mas para os oceanos de novatos que vêm para essas ideias via Redux, será uma revelação.

@bgnorlov esse é um bom ponto que é impossível armazenar referências circulares com Immutable.js, nunca pensei nisso! Eu acho que é um bom recurso.

Atualmente, tendemos a armazenar quase tudo no Redux, mesmo que seja um estado específico de visão transiente que nunca usamos fora de seu contexto (por exemplo, estado para uma instância redux-form ). Na maioria dos casos, podemos mover esse estado de redux sem sofrer nenhum problema. Mas a vantagem é que ele está lá, caso precisemos de componentes externos para responder a ele no futuro. Por exemplo, poderíamos ter algum ícone na barra de navegação que muda quando algum formulário tem erros ou está sendo enviado.

Eu diria que a coisa mais importante a se manter em mente é que colocar todos os estados normalizados no Redux (ou seja, qualquer coisa que precise ser juntada para renderizar as visualizações) tornará mais fácil de usar. O estado de que não precisa ser unido a nada provavelmente pode viver fora do Redux sem lhe causar dificuldades, mas geralmente não custa colocá-lo no Redux também.

FWIW, os documentos apontam que nem tudo precisa ir para o Redux, por http://redux.js.org/docs/faq/OrganizingState.html#organizing -state-only-redux-state.

Tem havido um pouco de conversa recente online sobre o que o Redux é "apropriado". Algumas pessoas veem benefícios em colocar literalmente tudo no Redux, outras acham que isso é muito incômodo e querem apenas armazenar dados recuperados de um servidor. Portanto, definitivamente não há nenhuma regra fixa aqui.

Como sempre, se as pessoas tiverem ideias para melhorar os documentos, os RP são totalmente bem-vindos :)

Se você precisar reutilizar redutores e ser capaz de alocar "sub-estados" em seu estado de redux, desenvolvi um plugin para fazer isso sem esforço (com integração React)

https://github.com/eloytoro/react-redux-uuid

@eloytoro IMO se você tiver problemas com algum redutor terceirizado com tipos de ação codificados ou localização no estado, você deve abrir um problema no projeto ... em breve isso se tornará uma prática de design inaceitável conforme as pessoas aprendem o redutor / ação reutilizável padrão do criador.

@eloytoro Eu ia escrever algo assim. Muito obrigado pela referência!

@ jedwards1211 Acho que você entendeu errado. Eu não mencionei redutores de terceiros ou problemas com eles de forma alguma, apenas tentando mostrar como meu snippet pode resolver o problema de tornar redutores reutilizáveis ​​em coleções com tamanhos dinâmicos

@avesus espero que funcione para você

@eloytoro ah, isso faz mais sentido.

No ano passado criei um jogo bastante complexo usando Redux. Eu também participei de outro projeto que não usa Redux, mas em vez disso, uma estrutura minimalista e personalizada que separa tudo em UI Stores e Server Stores (incluindo entradas). Sem entrar em muitos detalhes, minhas conclusões até agora são as seguintes:

O estado único é sempre melhor, no entanto, não exagere. Obter cada keyDown () através do armazenamento e de volta para a visualização é simplesmente confuso e desnecessário. Portanto, os estados transientes devem ser tratados pelos estados dos componentes locais (como o do React).

Eu acho que como o redux e o react inflamavam a web observável, a quantidade de boa animação nas páginas diminuiu.

@ mib32 você pode estar certo! Esperançosamente, as pessoas acabarão se acostumando a criar boas animações no React.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

captbaritone picture captbaritone  ·  3Comentários

rui-ktei picture rui-ktei  ·  3Comentários

ms88privat picture ms88privat  ·  3Comentários

benoneal picture benoneal  ·  3Comentários

jimbolla picture jimbolla  ·  3Comentários