Atualizações de matriz imutável com Array.prototype.with

Os navegadores ganharam recentemente um novo método interoperável que pode ser chamado em matrizes: Array.prototype.with().

Browser Support

  • Chrome: 110.
  • Edge: 110.
  • Firefox: 115.
  • Safari: 16.

Source

Neste artigo, explicamos como esse método funciona e como usá-lo para atualizar uma matriz sem alterar a matriz original.

Introdução ao Array.prototype.with(index, value)

O método Array.prototype.with(index, value) retorna uma cópia da matriz em que ele é chamado com o index definido como o novo value fornecido.

O exemplo a seguir mostra uma matriz de idades. Você quer criar uma nova cópia da matriz e mudar a segunda idade de 15 para 16:

const ages = [10, 15, 20, 25];

const newAges = ages.with(1, 16);
console.log(newAges); // [10, 16, 20, 25]
console.log(ages); // [10, 15, 20, 25] (unchanged)

Analisando o código:: ages.with(...) retorna uma cópia da variável ages sem modificar a matriz original. ages.with(1, …) substitui o segundo item (index = 1). ages.with(1, 16) atribui o segundo item a 16.

Assim, você conseguiu criar uma nova cópia da matriz com uma modificação.

Isso é muito útil quando você quer garantir que a matriz original permaneça inalterada, e este artigo aborda alguns dos casos de uso para isso. Mas, por enquanto, veja o que teria acontecido se você tivesse usado a notação de colchetes:

const ages = [10, 15, 20, 25];

const newAges = ages;
newAges[1] = 16;
console.log(newAges); // [10, 16, 20, 25]
console.log(ages); // [10, 16, 20, 25] (Also changed 🙁)

Como você pode ver, a variável ages também foi modificada neste exemplo. Isso acontece porque, quando você atribui ages = newAges, o JavaScript não copia a matriz, mas cria uma referência à outra matriz. Portanto, qualquer mudança em um também vai afetar o outro, já que ambos apontam para a mesma matriz.

Array.prototype.with() e imutabilidade

A imutabilidade está no centro de muitas bibliotecas e frameworks de front-end, como React (e redux) e Vue.

Além disso, outras bibliotecas e frameworks não exigem necessariamente imutabilidade, mas a incentivam para melhorar o desempenho: Angular e Lit.

Por isso, os desenvolvedores muitas vezes precisavam usar outros métodos que retornavam cópias de matrizes, o que prejudicava a legibilidade do código:

const ages = [10, 15, 20, 25];

const newAges = ages.map((age, index) => {
    if (index === 1) {
         return 16;
    }
    return age;
});

console.log(newAges); // [10, 16, 20, 25]
console.log(ages); // [10, 15, 20, 25] (Remains unchanged)

Confira um exemplo no Codepen de como .with() pode ser usado no React em combinação com useState para atualizar de forma imutável uma matriz de itens:

Como o método .with() retorna uma cópia da matriz, é possível encadear várias chamadas de .with() ou até mesmo outros métodos de matriz. O exemplo a seguir demonstra o incremento da segunda e da terceira idades da matriz:

const ages = [10, 15, 20, 25];

const newAges = ages.with(1, ages[1] + 1).with(2, ages[2] + 1)

console.log(newAges); // [10, 16, 21, 25]
console.log(ages); // [10, 15, 20, 25] (unchanged)

Outros novos métodos imutáveis

Três outros métodos se tornaram interoperáveis recentemente:

De acordo com a MDN, esses três métodos são a versão de cópia das contrapartes. Esses métodos também podem ser usados quando a imutabilidade é esperada ou preferida.

Em resumo, as atualizações imutáveis podem ser feitas com mais facilidade em JavaScript usando um dos quatro métodos apresentados neste artigo. Especificamente, o método .with() facilita a atualização de um único elemento da matriz sem alterar a matriz original.