Apresentando o Immer: Imutabilidade, o caminho fácil

As estruturas de dados estruturadas e imutáveis ??são um ótimo paradigma para armazenar o estado. Especialmente quando combinado com uma arquitetura de sourcing de eventos. No entanto, há um custo para pagar. Em um idioma como o JavaScript, onde a imutabilidade não é incorporada ao idioma, produzir um novo estado do anterior é uma tarefa chata e de caldeira. Para comprovar o ponto: A página Redux-ecosystem-links apenas lista pacotes 67 (!) Para ajudá-lo a lidar com estruturas de dados imutáveis ??no Redux.

E ainda; a maioria deles não resolve o problema da raiz: falta de suporte ao idioma. Por exemplo, onde update-in é um conceito elegante em um idioma como o ClojureScript, todas as contrapartes de JavaScript dependerão basicamente de caminhos de cadeia feios. Que são propensos a erros, difíceis de verificar o tipo e requer aprender mais um conjunto de funções da API de cor para serem pro-eficientes.

Então, e se parássemos em lutar contra o idioma e abraçássemos isso? Sem renunciar à elegância proporcionada por estruturas de dados persistentes. Isso é exatamente o que a immer faz.

Dica: se você não gosta de ler, você também pode assistir o tutorial do egghead para immer

Produtores

O Immer trabalha escrevendo produtores e o produtor mais simples possível parece assim:

Um produtor mínimo (vazio) retornará o estado original

A função de produção leva dois argumentos. O atual estado e uma função de produtor . O estado atual determina nosso ponto de partida, e o produtor expressa o que precisa acontecer com ele. A função de produtor recebe um argumento, o rascunho , que é um proxy para o estado atual em que você passou. Qualquer modificação que você fizer no rascunho será gravada e usada para produzir o próximo estado . O atual estado será intacto durante este processo.

Porque o Immer usa compartilhamento estrutural , e nosso exemplo de produtor acima não modificou nada, o próximo estado acima é simplesmente o estado com o qual começamos.

Vejamos o que acontece quando começamos a modificar o rascunho em nosso produtor. Observe que a função do produtor não retorna nada, o único que importa são as mudanças que fazemos.

Um verdadeiro produtor. Todas as alterações ao rascunho são refletidas no próximo estado, que compartilha estruturalmente itens intactos com o estado anterior

Aqui, realmente vemos produzir em ação. Criamos uma nova árvore de estados, que contém um item extra todo. Além disso, o status do segundo processo foi alterado. Estas onde as mudanças que aplicamos ao rascunho, e estão bem refletidas no próximo estado resultante.

Mas há mais. As últimas declarações na lista mostram muito bem que as partes do estado que foram modificadas no rascunho resultaram em novos objetos. No entanto, as partes inalteradas são estruturalmente compartilhadas com o estado anterior. O primeiro todo neste caso.

Um redutor com um produtor

Agora aprendemos o básico de produzir um novo estado. Vamos aproveitar isso em um redutor Redux exemplar. A próxima versão é baseada no exemplo oficial do carrinho de compras e carrega um monte de (possivelmente) novos produtos no estado. Os produtos são recebidos como uma matriz, transformados usando redução e, em seguida, armazenados em um mapa com seu id como chave.

Um redutor Redux típico

A parte do caldeirão aqui é:

  1. Nós temos que construir um novo objeto de estado, no qual o estado base seja preservado e o mapa de novos produtos seja misturado. Não é tão ruim neste caso simples, mas esse processo deve ser repetido para cada ação e em todos os níveis no qual queremos modificar algo.
  2. Temos de ter certeza de retornar o estado existente se o redutor não fizer nada

Com o Immer, só precisamos raciocinar sobre as mudanças que queremos fazer relativamente ao estado atual. Sem precisar fazer o esforço para produzir o próximo estado. Então, quando usamos produtos no redutor, nosso código simplesmente se torna:

Simplificando o redutor usando Immer

Observe quanto mais fácil é entender o que RECEIVE_PRODUCTS está realmente fazendo? O ruído foi largamente removido. Observe também que não lidamos com o caso padrão. Não alterar o rascunho simplesmente equivale a retornar o estado base. Tanto o redutor original como o novo se comportam exatamente o mesmo.

Sem condições

A idéia de produzir o próximo estado imutável modificando um rascunho temporário não é nova. Por exemplo, o JJ imutável fornece um mecanismo similar: withMutations . A grande vantagem da Immer no entanto, é que você não precisa aprender (nem carregar) uma nova biblioteca inteira para suas estruturas de dados. O Immer opera em objetos e arrays JavaScript normais.

As vantagens vão ainda mais longe. Para reduzir o boilerplate, ImmutableJS e muitos outros permitem que você expresse atualizações profundas (e muitas outras operações) com métodos dedicados. Esses caminhos, no entanto, são strings em bruto e não podem ser verificados por tipos-verificadores. Eles são bastante propensos a erros. Na lista a seguir, por exemplo, o tipo de list não pode ser inferido no caso ImmutableJS. Outras bibliotecas levam isso mesmo um passo adiante e até mesmo manipulam seus próprios DSLs nessas consultas de caminho, permitindo comandos mais complexos como emendas. Com o custo de introduzir um mini-idioma na língua.

Immer continua digitado fazendo atualizações profundas

Immer não sofre com nada disso; Funciona em estruturas de JavaScript incorporadas. Perfeitamente compreendido por qualquer tipo de verificador. E a modificação dos dados é feita através de APIs que você já conhece; os incorporados ao idioma.

Congelamento automático

Outra característica legal ( ? ) do Immer é que ele congelará automaticamente qualquer estrutura de dados que você criou usando o produce . (No modo de desenvolvimento). Para que você obtenha dados verdadeiramente imutáveis. Onde congelar todo o estado seria bastante caro, o fato de que o Immer pode simplesmente congelar as peças alteradas torna bastante eficiente. E, se todo o seu estado for produzido por funções de produce , o resultado efetivo será que seu estado inteiro está sempre congelado. O que significa que você obterá uma exceção quando tentar modificar o estado de qualquer forma.

Currying

Está bem. Um último recurso: Até agora, sempre chamamos de produce com dois argumentos, o baseState e uma função de producer . No entanto, em alguns casos, pode ser conveniente usar uma aplicação parcial. É possível chamar o produce com apenas a função de produtor. Isso criará uma nova função que executará o produtor quando for passada em um estado. Esta nova função também aceita uma quantidade arbitrária de argumentos adicionais e os transmite ao produtor.

Não se preocupe se você não pudesse analisar as últimas frases. O que se resume é que você pode reduzir ainda mais o calibre dos redutores alavancando o curry:

Um produtor de curry (veja também a listagem anterior para comparação)

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *