Auxiliares de Imutabilidade
Nota:
update
é um add-on legado. Em vez disso, useimmutability-helper
.
Importando
import update from 'react-addons-update'; // ES6
var update = require('react-addons-update'); // ES5 com npm
Visão geral
O React permite que você use qualquer estilo de gerenciamento de dados que desejar, incluindo mutação. No entanto, se você puder usar dados imutáveis em partes críticas de desempenho de seu aplicativo, é fácil implementar um método rápido shouldComponentUpdate()
para acelerar significativamente seu aplicativo.
Lidar com dados imutáveis em JavaScript é mais difícil do que em linguagens projetadas para isso, como Clojure. No entanto, nós fornecemos um simples auxiliar de imutabilidade, update()
, que torna muito mais fácil lidar com esse tipo de dados, sem alterar fundamentalmente a forma como seus dados são representados. Você também pode dar uma olhada no Facebook Immutable-js e na seção de desempenho avançado para obter mais detalhes sobre Immutable-js.
A ideia principal
Se você alterar dados assim:
myData.x.y.z = 7;
// ou...
myData.a.b.push(9);
Você não tem como determinar quais dados foram alterados desde que a cópia anterior foi substituída. Em vez disso, você precisa criar uma nova cópia de myData
e alterar apenas as partes que precisam ser alteradas. Então você pode comparar a cópia antiga de myData
com a nova em shouldComponentUpdate()
usando triplos iguais:
const newData = deepCopy(myData);
newData.x.y.z = 7;
newData.a.b.push(9);
Infelizmente, cópias profundas são custosas e às vezes impossíveis. Você pode aliviar isso copiando apenas os objetos que precisam ser alterados e reutilizando os objetos que não foram alterados. Infelizmente, no JavaScript de hoje, isso pode ser complicado:
const newData = extend(myData, {
x: extend(myData.x, {
y: extend(myData.x.y, {z: 7}),
}),
a: extend(myData.a, {b: myData.a.b.concat(9)})
});
Embora isso tenha um bom desempenho (já que apenas faz uma cópia superficial de objetos log n
e reutiliza o resto), é um grande trabalho escrever. Olhe para toda a repetição! Isso não é apenas irritante, mas também fornece uma grande área de superfície para bugs.
update()
update()
fornece “syntactic sugar” simples em torno deste padrão para tornar a escrita deste código mais fácil. Este código se torna:
import update from 'react-addons-update';
const newData = update(myData, {
x: {y: {z: {$set: 7}}},
a: {b: {$push: [9]}}
});
Embora a sintaxe demore um pouco para se acostumar (pelo fato de ser inspirada na linguagem de consulta do MongoDB), não há redundância, é estaticamente analisável e não tem muito mais digitação do que a versão mutativa.
As chaves prefixadas com $
são chamadas de comandos (command). A estrutura de dados que eles estão “mutando” é chamada de destino (target).
Comandos disponíveis
{$push: array}
push()
todos os itens emarray
no destino.{$unshift: array}
unshift()
todos os itens emarray
no destino.{$splice: array of arrays}
para cada item emarrays
chamasplice()
no destino com os parâmetros fornecidos pelo item.{$set: any}
substitui o destino inteiramente.{$merge: object}
mescla as chaves deobject
com o destino.{$apply: function}
passa o valor atual para a função e o atualiza com o novo valor retornado.
Exemplos
Push simples
const initialArray = [1, 2, 3];
const newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4]
initialArray
ainda é [1, 2, 3]
.
Coleções aninhadas
const collection = [1, 2, {a: [12, 17, 15]}];
const newCollection = update(collection, {2: {a: {$splice: [[1, 1, 13, 14]]}}});
// => [1, 2, {a: [12, 13, 14, 15]}]
Isso acessa o índice 2
da collection
, chave a
, e faz uma junção de um item começando no índice 1
(para remover 17
) enquanto insere 13
e 14
.
Atualizando um valor com base no valor atual
const obj = {a: 5, b: 3};
const newObj = update(obj, {b: {$apply: function(x) {return x * 2;}}});
// => {a: 5, b: 6}
// Isso é equivalente, mas fica detalhado para coleções profundamente aninhadas:
const newObj2 = update(obj, {b: {$set: obj.b * 2}});
Mesclagem (Superficial)
const obj = {a: 5, b: 3};
const newObj = update(obj, {$merge: {b: 6, c: 7}}); // => {a: 5, b: 6, c: 7}