Redux DIY com RxJS => RxDx

Öner Zafer Blocked Unblock Seguir Seguindo 29 de dezembro de 2018

O gerenciamento do estado é essencial se você estiver desenvolvendo um aplicativo enorme e usar o gerenciamento de estado adequado pode ser um verdadeiro salva-vidas. No React, o Redux é nosso salva-vidas, e no Angular, temos o NgRx para esse papel.

Eu acho que, no nível arquitetônico, ambos têm boilerplates similares e se você já compreende um, sua curva de aprendizado para o outro será mais suave. O que eu realmente gosto no NgRx não é a biblioteca em si, mas o RxJS, que é realmente a biblioteca mais incrível para lidar com os eventos de streaming.

Enquanto investigava sobre Redux e NgRx, percebi que quase não há pessoas que usam o Redux por si só, sem muitas bibliotecas auxiliares, mas, por outro lado, o caso não é semelhante para NgRx, pois fornece tudo em uma solução na maioria dos casos. os casos. Então, decidi fazer uma experiência para combinar o RxJS e todas as bibliotecas auxiliares do Redux para criar uma biblioteca de gerenciamento de estado super duper para o React com impressões digitais similares, como o Redux.

Neste ponto, a receita ficou óbvia: Primeiramente, eu reimplementei o Redux com o RxJS e adicionei um pouco de middleware em cima dele e ele está pronto para servir! Então, com este post, vou tentar explicar como fiz isso.

Sugestão: já lancei a versão alfa da biblioteca resultante e pode verificá-la aqui .

Passo 0: Desconstruir Redux e Bibliotecas Relacionadas

O Redux fornece principalmente uma função createStore que obviamente cria uma loja. Esta createStore tem um redutor, estado inicial e um intensificador .

Redutor é uma função que recebe um estado e retorna um estado. Muito simples, certo? Mas espere um minuto, precisarei de mais de um redutor, o que significa que precisarei de uma função auxiliar para combinar todos os redutores em um grande redutor de gordura.

Para um propósito similar, o Redux tem a função combineReducers , que é muito útil. Um estado inicial é um objeto que pode ser um estado vazio ou indefinido. Ainda mais simples! O enhancer é um pouco estranho que tem a seguinte assinatura:

 (createStore) => (reducer, initialState) => loja 

Mais uma vez vou precisar de mais de um enhancer e novamente precisarei de uma função auxiliar para combinar todos os enhancers em um que é chamado applyMiddleware.

Quando executamos a função createStore, ela retorna um objeto semelhante ao objeto simplificado abaixo:

 { 
getState: () => estado,
inscreva-se: () => cancelar a assinatura,
despacho: (ação) => anular
}

getState retorna o estado atual. Subscribe retorna um objeto de cancelamento de assinatura, mas se eu estiver planejando usar o RxJS, posso usar sua própria assinatura e cancelamento de assinatura. Como resultado, terei uma implementação mais simples. Funções de despacho , na verdade, passam a ação para o redutor. E novamente o RxJS se tornará acessível aqui, o qual possui seu próprio mecanismo de despacho.

Então, criei uma lista de tarefas para atingir meu objetivo:

  • Implementar `createStore (reducer, initialState, enhancer) => armazenar`
  • Implemente `combineReducer (structuredReducersObject) => redutor`
  • Implementar `applyMiddleware (createStore) => (redutor, initialState) => armazenar`

Vamos começar!

Etapa 1: createStore com a ajuda de RxJS BehaviourSubject e operadores

A qualquer momento, um consumidor da loja chama getState, store deve retornar o estado atual de volta. Se eu usar BehaviourSubject como fonte de estado, a implementação do estado de armazenamento interno será mais fácil, uma vez que o comportamento semelhante dos observáveis RxJS pertence a BehaviourSubject chamando BehaviourSubject.value .

Assim que se faz:

Reescreverei o que fiz aqui nas palavras humanas. Na função createStore , criei uma Store com reducer e initialState . Se houver um enhancer, eu passo uma falsa função createStore que retornará o Store já criado, como resultado, ele retornará um Store melhorado. Em seguida, envio a primeira ação e retorno a loja criada ou aprimorada. Então, quando a função createStore é chamada, podemos usar a referência de loja em nosso aplicativo. Talvez essa referência possa ser passada para algum provedor de contexto reagente.

Como você pode ver, a parte mais complicada é a função select e não é um recurso obrigatório. Eu adicionei essa parte para ter uma interface, como selecionar novamente . Como eu posso entender até agora, a função de subscrição do Redux não é um recurso amplamente usado, mas a biblioteca react-redux a usa para obter as atualizações e renderizar o componente (se alguém usá-lo diretamente, por favor comente abaixo, porque no meu experimento Não encontrei nenhum caso de uso para minha função de assinatura).

Então, a função createStore até agora deve ser capaz de trabalhar para um redutor e um intensificador, o que significa que o backbone da minha biblioteca Redux baseada em RxJS está pronto para ser nomeado: De agora em diante eu chamarei de RxJs + Redux = RxDx.

Etapa 2: Como criar uma função combineReducers?

Quando eu verifiquei o código-fonte do combineReducers do Redux, percebi que é necessário um objeto (que pode ser aninhado) do qual as chaves têm um redutor e como resultado retorna um redutor maior.

Vamos começar com o seguinte código inicial:

Por enquanto, vou evitar a prevenção de erros e o tratamento de erros (vou confiar em quem usar essa função). A idéia aqui é percorrer as teclas do objeto redutor e chamar o redutor anexado com a parte de estado relacionada e a ação.

Como resultado, todos os redutores atualizarão somente a parte relacionada do estado e eu combinarei os resultados na mesma estrutura e os retornarei. Enquanto eu estava tentando codificar essa função, notei que o nome da função tem a solução em si: Array.reduce.

Etapa 3: A função Magical applyMiddleware

Se você der uma olhada mais de perto na implementação do Redux, a maior mágica acontece com apenas uma função chamada compor . O que a função de composição faz? Na verdade, é bem simples, pois é preciso uma lista de funções, envolve cada uma delas passando a segunda para a primeira, a terceira para a segunda e assim por diante, e finalmente retorna essa função aninhada estranha:

Como você pode ver acima, as funções a, b, c, d, e se tornam A (B (C (D (E (… args))))). Este comportamento é crucial porque todos os potenciadores devem ser capazes de ser chamados em cada ação e todos eles devem ser capazes de alterar a ação ou despachar uma nova ação ou mesmo atrasar a ação original. Há mais uma restrição aos intensificadores, pois eles devem seguir a assinatura abaixo:

 // sample enhancer signature 
Const enhancer => (createStore) => (next) => (ação) => estado

Finalmente, acabei com a seguinte função, omitindo as partes de tratamento de erros e prevenção de erros:

Palavras finais

Na verdade, o RxDx inclui atualmente a perda de outras funcionalidades e, se você estiver disposto a vê-las e usar todos os exemplos, poderá verificar o repo e o exemplo de repo de todo o aplicativo .

Esta é a parte principal do RxDx e estou planejando explicar como conectar os componentes RxDx a React com um artigo de acompanhamento a este.

Com o artigo de acompanhamento, espero lançar alguma luz sobre a estrutura interna do reagente-redux e, em seguida, irei avançar para reagir observável e, em seguida, selecionar novamente as bibliotecas.

Se você está interessado em RxDx, e gostou do artigo até agora, por favor, não esqueça de bater no aplauso (você pode bater palmas até 50 vezes). Se você compartilhar suas idéias e comentários abaixo, eu ficaria feliz! Obrigado pela leitura.

Texto original em inglês.