Programação Funcional em JS, Imutabilidade e Reducers com Immer
Published: 2021-11-11 13:06:56



## Programação Funcional

Resumidamente podemos dizer que as linguagens que trabalham com o paradigma de programação funcional seguem alguns preceitos básicos:

- __Funções puras:__
  Recebem as informações que precisam por parâmetros e retornam sempre o mesmo resultado se chamadas com os mesmos parâmetros;
- __Sem efeitos colaterais:__
  As funções não mutam informações fora de seus escopos;
- __Stateless:__
  As funções não possuem e não dependem de estados internos ou externos;
- __Idempotentes:__
  As funções podem ser chamadas um número infinito de vezes e em diferentes contexto que resultado vai ser sempre o mesmo se os argumentos forem os mesmos, não gerando side-effects nem persistindo nenhuma alteração.

## Programação Funcional em Javascript

Javascript por padrão não é uma linguagem funcional, porém por ser uma linguagem altamente flexível, adaptável e independente de paradigmas, os padrões de Programação Funcional podem ser utilizados em JS. É importante observar no entanto, que por ser uma linguagem extremamente permissiva e não te obrigar a praticamente nada, o Javascript não vai garantir que seu código siga todos os preceitos da PF. Fica a cargo do desenvolvedor aplicar esses preceitos corretamente.

__Função não pura e com side effect em JS__

```js
var externalCount = 0;

function plus1() {
  externalCount = externalCount + 1;
  return externalCount;
}

console.log('External count:', externalCount); // External count: 0
var internalCount = plus1();
console.log('Internal count:', internalCount); // Internal count: 1
console.log('External count:', externalCount); // External count: 1
```

### Programação Funcional em JS antes do ES5

Antes do _ES5_ ([ECMAScript 5th Edition]) os conceitos de Programação Funcional praticamente não existiam em JS e só poderiam ser obtidos replicando manualmente esses comportamentos em seu código ou utilizando libs externas que faziam exatamente isso (ex, _[Underscore.js]_).

### Programação Funcional em JS com ES5 (~ 2012*)

O _ES5_ trouxe para o mundo do JS alguns conceitos e funcionalidades de Programação Funcional, como por exemplo as funções _map_, _filter_ e _reduce_, possibilitando aplicar esse novo paradigma onde antes não era possível. As revisões futuras do ES vieram a reforçar esse suporte.

\* _O ES5 foi oficialmente criado em 2009, mas só começou mesmo a ser utilizado por volta de 2012 devido à incompatibilidade com a base instalada de navegadores antigos._

__Filtrando antes do ES5__

```js
var allNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

var evenNumbers = [];

for(var i = 0; i < allNumbers.length; i++) {
  if(allNumbers[i] % 2 === 0) {
    evenNumbers.push(allNumbers[i]);
  }
}

// evenNumbers = 0, 2, 4, 6, 8
```

__Filtrando com o ES5__

```js
var allNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

var evenNumbers = allNumbers.filter(function(number) {
  return number % 2 === 0
});

// evenNumbers = 0, 2, 4, 6, 8
```

## Imutabilidade

Imutabilidade em seu conceito mais básico diz que uma informação após ser definida não pode ser mais alterada.
Em programação isso traz algumas vantagens, como por exemplo garantir a consistência da informação durante todo o fluxo de dados, não gerar efeitos colaterais em funções, poder fazer referências à informação original ao invés de copiá-la, poder usar essa informação como índice ou chave, passá-la pra múltiplos threads e funções assíncronas, etc.

## Imutabilidade em JS

### Valor x referência

Em Javascript a maioria dos parâmetros são passados __por referência__, ou seja, ao invés de serem copiados, uma referência à informações original será criada. Isso significa que qualquer alteração feita nesse parâmetro refletirá também na informação original em si e vice-versa.

### Tipos primitivos

A exceção à essa regra são os __tipos primitivos__ da linguagem Javascript, que sempre são passados como __valor__, sem nenhuma referência à informação original. São eles:
`Boolean`, `Number`, `String`, `Null`, `Undefined` e `Symbol`.

### Tipos Object

Tudo que não for um tipo primitivo em JS é do tipo `Object` e esse tipo sempre é passado por referência, portanto __NÃO__ é imutável.

__Tipos primitivos não mutam__

```js
var externalCount = 0;

function plus1(count) {
  count = count + 1;
  return count;
}

console.log('External count:', externalCount); // External count: 0
var internalCount = plus1(externalCount);
console.log('Internal count:', internalCount); // Internal count: 1
console.log('External count:', externalCount); // External count: 0
```

__Já os tipos não primitivos (Object) mutam__

```js
var externalCount = { 
  value: 0,
};

function plus1(count) {
  count.value = count.value + 1;
  return count;
}

console.log('External count:', externalCount.value); // External count: 0
var internalCount = plus1(externalCount);
console.log('Internal count:', internalCount.value); // Internal count: 1
console.log('External count:', externalCount.value); // External count: 1
```

### Então como obter imutabilidade em JS?

#### Pré ES5

Até o advento do _ES5_ era praticamente inexistente o conceito de imutabilidade em JS, pelo menos de forma nativa, tendo que recorrer a libs externas que de alguma maneira tentavam emular esse comportamento.

### ES5

#### Object.assign

O _ES5_ introduziu o método `assign` aos objetos JS, que permite copiar o conteúdo de um número _N_ de objetos à direita ao objeto mais à esquerda, mutando o mesmo e retornando o resultado. Note que somente o objeto mais à esquerda é mutado, os demais não.

__Object.assign mutando o objeto mais à esquerda__

```js
var nameOnly = {
  name: 'Renato',
};

var lastNameOnly = {
  lastName: 'Rodrigues',
}

var emailOnly = {
  email: 'renato.rodrigues@renatorodrigues.com',
}

var myInfo = Object.assign(nameOnly, lastNameOnly, emailOnly);

// nameOnly = { name: 'Renato', lastName: 'Rodrigues', email: 'renato.rodrigues@renatorodrigues.com' }
// myInfo = { name: 'Renato', lastName: 'Rodrigues', email: 'renato.rodrigues@renatorodrigues.com' }
```

_Tá, mas e a imutabilidade?_ Lembrando que quando falamos de JS, a linguagem nos permite fazer praticamente qualquer coisa, temos então um pulo do gato aí. Em nenhum lugar é dito que o objeto mais à esquerda precisa ser uma referência a um objeto previamente criado. Ele pode ser simplesmente um objeto vazio, recém criado, que vai receber todos os valores à direita. Logo, se não existe uma referência pra ele, não existe o que mutar. ¯\\_(ツ)_/¯

__Object.assign NÃO mutando o objeto mais à esquerda__

```js
var myInfo = Object.assign({}, nameOnly, lastNameOnly, emailOnly);

// nameOnly = { name: 'Renato' }
// myInfo = { name: 'Renato', lastName: 'Rodrigues', email: 'renato.rodrigues@renatorodrigues.com' }
```

__Importante:__  O `Object.assign` somente copia as propriedades de primeiro nível dos objetos em si, caso uma propriedade seja um objeto _nested_ ou faça uma referência a um objeto externo elas __poderão__ ser mutadas, pois continuarão a apontar para o objeto original.

### ES6 (2015)

O _ES6_ ([ECMAScript 6th Edition], _a.k.a ES2015_) trouxe mais algumas funções e funcionalidades relacionadas à imutabilidade, apesar de _\*spoiler\*_ nenhuma delas garantir uma imutabilidade 100%.

#### Const

O _ES6_ trouxe ao JS o conceito de `constantes`, que em teoria são variáveis imutáveis, mas não se deixe enganar, o `const` só funciona assim com tipos __primitivos__. Com tipos __Object__ ele não permite que você reatribua um novo valor à variável, mas continua permitindo que as propriedades de um objeto já atribuído a ela sejam alteradas.

__Imutabilidade com Const__

```js
const name = 'Renato';

const person = {
  name: 'Renato',
}

name = 'Roberto'; // Error: Assignment to constant variable. 
person = { name: 'Roberto' }; // Error: Assignment to constant variable. 

person.name = 'Roberto' // person = { name: 'Roberto' }
```

#### Object.seal

O `Object.seal` "sela" um objeto, não permitindo que nenhuma propriedade seja removida e nenhuma nova seja adicionada a ele, porém permite que suas propriedades já existentes sejam modificadas.

__Importante__: nenhum erro ou warning é retornado quando se tenta mutar um objeto selado.

```js
const myInfo = {
  name: 'Renato',
  lastName: 'Rodrigues',
  email: 'renato.rodrigues@renatorodrigues.com',
};

Object.seal(myInfo);

delete myInfo.email;
myInfo.age = 90;
myInfo.name = "Roberto";

```

__Resultado quando não selado__

```js
myInfo = {
  name: 'Roberto',
  lastName: 'Rodrigues',
  age: 90,
}
```

__Resultado quando selado__

```js
myInfo = {
  name: 'Roberto',
  lastName: 'Rodrigues',
  email: 'renato.rodrigues@renatorodrigues.com',
}
```

#### Object.freeze

O `Object.freeze` "congela" um objeto, não permitindo que nenhuma propriedade seja removida, nenhuma nova seja adicionada a ele e nem que suas propriedades já existentes sejam modificadas.

__Importante__: nenhum erro ou warning é retornado quando se tenta mutar um objeto congelado.

```js
const myInfo = {
  name: 'Renato',
  lastName: 'Rodrigues',
  email: 'renato.rodrigues@renatorodrigues.com',
};

Object.freeze(myInfo);

delete myInfo.email;
myInfo.age = 90;
myInfo.name = "Roberto";
```

__Resultado quando congelado__

```js
const myInfo = {
  name: 'Renato',
  lastName: 'Rodrigues',
  email: 'renato.rodrigues@renatorodrigues.com',
}
```

__"Ah mas então isso é imutabilidade!"__ 

<p align="center" width="100%">
    <img width="200" src="https://user-images.githubusercontent.com/484549/114883627-7cac5700-9ddb-11eb-9951-1b9178c0dbbf.png"> 
</p>

Assim como no `Object.assign` (e no `Object.seal`), o `Object.freeze` também só vale para as propriedades de primeiro nível do objeto em si, caso uma propriedade seja um objeto _nested_ ou faça uma referência a um objeto externo seus filhos __poderão__ ser mutados, pois esse objeto não estará congelado.

__Objetos congelados com propriedades nested__

```js
const myInfo = {
  name: 'Renato',
  lastName: 'Rodrigues',
  email: 'renato.rodrigues@renatorodrigues.com',
  job: {
    title: "Self Employed",
    email: "renato@renatorodrigues.com",
  }
};

const jobAtSpaceX = {
  title: "VP of Mars Colonization",
  email: "renato.rodrigues@wearecoming.mars",
};

Object.freeze(myInfo);

myInfo.job = jobAtSpaceX;
myInfo.job.wage = undefined;
```

__Resultado obtido__

```js
myInfo = { 
  name: 'Renato', 
  lastName: 'Rodrigues', 
  email: 'renato.rodrigues@renatorodrigues.com', 
  job: { 
    title: 'Self Employed', 
    email: 'renato.sp@gmail.com', 
    wage: undefined 
  } 
} 
```

__Objetos congelados com referência externa__

```js
const jobAtSpaceX = {
  title: "VP of Mars Colonization",
  email: "renato.rodrigues@wearecoming.mars",
};

const myInfo = {
  name: 'Renato',
  lastName: 'Rodrigues',
  email: 'renato.rodrigues@renatorodrigues.com',
  job: jobAtSpaceX,
};

Object.freeze(myInfo);

jobAtSpaceX.wage = Infinity;
```

__Resultado obtido__

```js
myInfo = { 
  name: 'Renato', 
  lastName: 'Rodrigues', 
  email: 'renato.rodrigues@renatorodrigues.com', 
  job: { 
    title: 'VP of Mars Colonization', 
    email: 'renato.rodrigues@wearecoming.mars', 
    wage: Infinity,
  } 
}
```

### ES6 (para Arrays) e E2018 (para Objetos)

#### Spread operator

O Spread operador se tornou o meio padrão de conseguir imutabilidade em JS, pois ao _"espalhar"_ as propriedades de um `array` _([ES6][ECMAScript 6th Edition])_ ou `objeto` _([ES2018][ECMAScript 9th Edition])_ em um novo array ou objeto, uma cópia é feita, se tornando assim independente do original.  

__Mergeando objetos com o Spread__

```js
var nameOnly = {
  name: 'Renato',
};

var lastNameOnly = {
  lastName: 'Rodrigues',
}

var emailOnly = {
  email: 'renato.rodrigues@renatorodrigues.com',
}

var myInfo = { ...nameOnly, ...lastNameOnly, ...emailOnly };

// nameOnly = { name: 'Renato' }
// myInfo = { name: 'Renato', lastName: 'Rodrigues', email: 'renato.rodrigues@renatorodrigues.com' }
```

__"Ah, mas então está aí a imutabilidade 100% em JS"__

<p align="center" width="100%">
    <img width="200" src="https://user-images.githubusercontent.com/484549/114885941-859e2800-9ddd-11eb-8945-8e8b963cf31d.png"> 
</p>

O Spread Operator é como se fosse uma "versão moderna" do `Object.assign({}, N)`. A mesma situação com propriedades nested e referências externas citadas anteriormente também ocorre com o spread.

<p align="center" width="100%">
    <img width="300" src="https://user-images.githubusercontent.com/484549/114887631-027dd180-9ddf-11eb-8a5a-aa04590c0412.png"> 
</p>

## Proxies

Uma das novidades mais interessantes (e menos utilizadas) do _ES6_ foram os proxies. Podemos dizer que quando se faz proxy de um objeto, ele fica "protegido" por um proxy que tem acesso à leitura e à gravação de suas propriedades e pode manipular como fazê-los. Resumindo, o proxy "esconde" o objeto original e qualquer acesso à ele é feito via métodos especiais chamados de traps. Isso pode ser utilizado juntamente com um padrão que favoreça a imutabilidade do objeto original, como por exemplo o [copy-on-write].

__Funcionamento prático do Proxy__

<p align="center" width="100%">
    <img width="500" src="https://user-images.githubusercontent.com/484549/113735547-b8a43580-96d2-11eb-9c60-659660038bf5.png"> 
</p>

__Exemplo Proxy__

```js
const personObj = {
  firstName: 'Renato',
  lastName: 'Rodrigues',
}

const handler = {
  set(target, property, value) { // set Trap
    if (Array.isArray(value)) {
      target[property] = value.join(', ');
    } else {
      target[property] = value;
    }
  },
  get(target, property) { // get Trap
    if (typeof target[property] === 'string' && target[property].match(/, /)) {
      return target[property].split(', ');
    } else {
      return target[property];
    }
  }
}

const personProxy = new Proxy(personObj, handler);

personObj.hobbies = "video games, running";
personProxy.pets = ["Mario", "Luigi"];

// personObj.hobbies = "video games, running"
// personObj.pets = "Mario, Luigi"

// personProxy.hobbies = ["video games", "running"]
// personProxy.pets = ["Mario", "Luigi"]
```

## Immer

Fazendo o uso massivo de proxies, a biblioteca __[Immer]__ surgiu como uma maneira mais simples e direta de garantir a imutabilidade de objetos em JS. Basicamente o que ela faz é proteger o objeto original _(olha o Proxy aí \o/)_ e persistir cada alteração feita nele num objeto intermediário, chamado de _Draft_, para depois no final da operação _produzir_ um novo objeto com o resultado das alterações do draft aplicadas ao objeto original.

__Funcionamento prático da Immer__

<p align="center" width="100%">
    <img width="500" src="https://user-images.githubusercontent.com/484549/113742336-e8563c00-96d8-11eb-9fe7-4ed7bad770ad.png"> 
</p>

### Mas como a Immer funciona?

<p align="center" width="100%">
    <img width="600" src="https://immerjs.github.io/immer/assets/images/immer-4002b3fd2cfd3aa66c62ecc525663c0d.png"> 
</p>

__Exemplo sem Immer (muta o objeto original)__

```js
const person = {
  firstName: 'Renato',
  lastName: 'Rodrigues',
}

const withCobli = (collaborator) => {
  collaborator.company = 'Cobli';
  return collaborator;
}

const employee = withCobli(person);

// person: { firstName: 'Renato', lastName: 'Rodrigues', company: 'Cobli' } 
// employee: { firstName: 'Renato', lastName: 'Rodrigues', company: 'Cobli' } 
```

__Exemplo com Spread (não muta o objeto original)__

```js
const person = {
  firstName: 'Renato',
  lastName: 'Rodrigues',
}

const withCobli = (collaborator) => {
  return {
    ...collaborator,
    company: 'Cobli',
  };
];

const employee = withCobli(person);

// person: { firstName: 'Renato', lastName: 'Rodrigues' } 
// employee: { firstName: 'Renato', lastName: 'Rodrigues', company: 'Cobli' } 
```

__Exemplo com Immer__

```js
const person = {
  firstName: 'Renato',
  lastName: 'Rodrigues',
}

const withCobli = produce(collaborator, draftCollaborator => {
  draftCollaborator.company = 'Cobli';
});

const employee = withCobli(person);

// person: { firstName: 'Renato', lastName: 'Rodrigues' } 
// employee: { firstName: 'Renato', lastName: 'Rodrigues', company: 'Cobli' } 
```

### Porque Immer?

De acordo com o artigo de introdução à Immer, somente na listagem do [Redux-ecosystem-links] existem outras __67__ bibliotecas de imutabilidade em JS. Então porque usar a Immer?

- __Pequena__: Somente 3kb e com __ZERO__ dependências;
- __Rápida__: Utiliza funções nativas de JS, como os proxies, ao invés de loops e condicionais;
- __Simples__: Pode ser implementada em uma linhas e não requer nenhuma configuração;
- __Versátil__: Suporta propriedades nested de infinitos níveis e mantem a tipagem correta das propriedades!
- __Fácil__: Curva de aprendizagem praticamente zero, somente sintaxe nativa de JS, nenhuma outra sintaxe ou padrão é necessário;
- __Transparente__: Uma vez implementada, você mal percebe que está usando;
- __Estruturalmente Equivalente__: Partes não mutadas do novo objeto vão continuar fazendo referência ao objeto original. Mantendo eles assim equivalentes e evitando a duplicação de informações idênticas na memória.

## Reducers

No mundo React existe um fluxo obrigatório de como as informações devem ser persistidas e lidas na Store (_state_). Esse fluxo é uni-direcional, com passos bem definidos e sempre trabalhando com imutabilidade _(viu a chance aí?)_. Ele se aplica tanto ao estado da aplicação _(usando Redux)_, quanto ao estado interno dos componentes _(usando useReducer)_

<p align="center" width="100%">
    <img width="500" src="https://user-images.githubusercontent.com/484549/113736047-2badac00-96d3-11eb-849e-4a77b43994df.png"> 
</p>

Um reducer nada mais é que uma função que recebe dois parâmetros, o estado atual e uma ação que contém um tipo e pode ou não conter um payload. A partir do tipo da ação é decidido quais mudanças serão aplicadas ao estado da aplicação/componente. Como este estado é __imutável__, esta função deve retornar uma nova cópia dele já com as diferenças aplicadas. Este então será o novo estado.

Hoje em dia, grande maioria dos devs utiliza o spread operator para fazer essa cópia do estado atual e modificá-lo sem mutá-lo

__Reducer usando spread__

```js
const demoReducer = (state: demoState, action: demoActions): demoState => {
  switch (action.type) {
    case ActionTypes.INCREMENT_COUNTER:
      return {
        ...state,
        count: state.count + 1
      };
    case ActionTypes.DECREMENT_COUNTER:
      return {
        ...state,
        decrementCount: state.decrementCount - 1
      };
    case ActionTypes.SET_AMOUNT:
      return {
        ...state,
        amount: action.payload
      };
    default:
      return state;
  }
};
```

__Reducer usando spread (erro)__

```js
const demoReducer = (state: demoState, action: demoActions): demoState => {
  switch (action.type) {
    case ActionTypes.INCREMENT_COUNTER:
      return {
        ...state,
        count: state.count++
      };
  }
};
```

## Currying

Currying é o processo de transformar uma função que espera vários argumentos em uma função que espera um único argumento e retorna outra função que recebe um argumento e assim sucessivamente até o final dos argumentos. Por exemplo, uma função que recebe três argumentos, vai resultar em uma função que recebe um argumento e retorna uma função que recebe um argumento, que por sua vez retorna uma função que recebe um argumento e retorna o resultado da função original.

As funções _curried_ podem tanto receber parâmetros das funções acima quanto os valores retidos nessas funções _(closures)_

__Exemplo Currying__

```js
function sayTo(who) {
  const person = who.toString().toUpperCase();

  return function say(what) {
    console.log(`Say ${what} to ${person}`)
  }
}

const sayToRenato = sayTo('Renato');
sayToRenato('Wasuuuup!'); 
// Say Wasuuuup! to RENATO 

const sayToCaio = sayTo('Caio');
sayToCaio('Yo Bro!')
sayToCaio('Whats going on?');
// Say Yo Bro! to CAIO
// Say Whats going on? to CAIO 
```

## Curried Reducer

Aproveitando as vantagens do currying, os desenvolvedores da Immer criaram o modo curried do método `produce`, que cai como uma luva quando usado em um Reducer. Ele recebe como o único parâmetro uma função de callback que vai receber um state e uma _action_. O curried reducer irá retornar uma função que ao receber o state original irá criar um draft dele e chamará o callback passando esse draft junto com a action passada pelo Redux/useReducer, criando assim um reducer 100% compatível e completamente transparente para quem o chama. 

__Exemplo Curried Reducer__

```js
import produce, { Draft } from "immer";

const demoReducer = produce((draft: Draft<DemoState>, action: DemoActions) => {
  switch (action.type) {
    case ActionTypes.INCREMENT_COUNTER:
      draft.count++;
      break;
    case ActionTypes.DECREMENT_COUNTER:
      draft.decrementCount--;
      break;
    case ActionTypes.SET_AMOUNT:
      draft.amount = action.payload;
      break;
  }
});
```

_(Sim, é só isso que precisa pra usar, não tem mais nada mesmo)_

## Como migrar para Immer

1) Importe a função `produce` da Immer;
2) Adicione-a no modo __curried__ ao seu reducer ja existente;
3) Remova todos os returns;
4) Remova todos os spreads;
5) Mute as propriedades diretamente;
6) Remova o case default.

<p align="center" width="100%">
    <img width="400" src="https://user-images.githubusercontent.com/484549/113737117-21d87880-96d4-11eb-9077-1dda6b4ec910.png"> 
</p>

### Desafios

1) Esquecer o padrão spread;
2) (Re-)aprender a mutar propriedades de um objeto usando JS básico.

__Na dúvida sempre lembre-se__

<p align="center" width="100%">
    <img width="500" src="https://user-images.githubusercontent.com/484549/113745551-c01c0c80-96db-11eb-9d3b-a45be55c9c16.png"> 
</p>

### Bonus: Mutações básicas

```js
// Alterar propriedade
draft.property = newValue;

// Criar propriedade
draft.newProperty = value;

// Excluir propriedade
delete draft.uselessProperty;

// Alterar/criar propriedade nested
draft.nestedObject.nestedProperty = value; 

// Excluir propriedade nested
delete draft.nestedObject.uselessNestedProperty;

// Alterar/criar propriedade nested em um array
draft.nestedArray[i].nestedProperty = value;

// Adicionar um item ao um array
draft.nestedArray.push(newObj);

// Excluir propriedade nested em um array
delete draft.nestedArray[i].uselessNestedProperty;

// Excluir um item ao um array
draft.nestedArray.splice(i, 1);
```

[Referência completa de mutação em objetos e arrays]


## FAQ

### Preciso migrar tudo?

Nop. A menos que queira.

### Qual o melhor momento para migrar então?

Considerando que a migração é bem simples, eu usaria a seguinte regra: Tem que modificar algo num Reducer? já aproveita a chance e migra.

## Ideias para o futuro

- <strike>Adicionar Reducers com Immer ao gerador de pacotes.</strike> _(done)_
- _\<insira sua ideia aqui\>_

## Exemplos de reducers migrados

- Sandbox Demo: [Antes][demo-reducer-before], [Depois][demo-reducer-after]
- [Reducer de EventStatus[] SSE][sse-reducer]
- [Reducer de Checklists][checklist-reducer]

## Apêndice 1: Igualdade de valores e referências

__Igualdade de valores e referências__
```js
var allNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

var evenNumbers = [];

for (var i = 0; i < allNumbers.length; i++) {
  if (allNumbers[i] % 2 === 0) {
    evenNumbers.push(allNumbers[i]);
  }
}

evenNumbers == allNumbers; // false
evenNumbers[0] === allNumbers[0]; // true
evenNumbers[1] === allNumbers[2]; // true
```

__Igualdade de valores e referências usando filter__
```js
var allNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

var evenNumbers = allNumbers.filter(function (number) {
  return number % 2 === 0
});

evenNumbers == allNumbers; // false
evenNumbers[0] === allNumbers[0]; // true
evenNumbers[1] === allNumbers[2]; // true
```

__Igualdade de valores e referências em objetos__
```js
  var allNumbers = [ 
    { v : 0 }, { v : 1 }, { v : 2 }, { v : 3 }, { v : 4 }, 
    { v : 5 }, { v : 6 }, { v : 7 }, { v : 8 }, { v : 9 }
  ];

  var evenNumbers = allNumbers.filter(function (item) {
      return item.v % 2 === 0
  });

  evenNumbers == allNumbers; // false
  evenNumbers[0] === allNumbers[0]; // true
  evenNumbers[0].v === allNumbers[0].v; // true
  evenNumbers[1] === allNumbers[2]; // true
  evenNumbers[1].v === allNumbers[2].v; // true
```

__Igualdade de valores e referências com Object.assign__
```js
var nameOnly = {
  name: 'Renato',
};

var lastNameOnly = {
  lastName: 'Rodrigues',
}

var emailOnly = {
  email: 'renato.rodrigues@renatorodrigues.com',
}

var myInfo = Object.assign(nameOnly, lastNameOnly, emailOnly);

myInfo.name === nameOnly.name; // true
myInfo.email === nameOnly.email; // true
myInfo === nameOnly; // true
```

__Igualdade de valores e referências com Object.assign({})__

```js
var nameOnly = {
  name: 'Renato',
};

var lastNameOnly = {
  lastName: 'Rodrigues',
}

var emailOnly = {
  email: 'renato.rodrigues@renatorodrigues.com',
}

var myInfo = Object.assign({}, nameOnly, lastNameOnly, emailOnly);

myInfo.name === nameOnly.name; // true
myInfo.email === nameOnly.email; // false
myInfo === nameOnly; // false
```

__Igualdade de valores e referências com o spread operator__
```js
var nameOnly = {
  name: 'Renato',
};

var lastNameOnly = {
  lastName: 'Rodrigues',
}

var emailOnly = {
  email: 'renato.rodrigues@renatorodrigues.com',
}

var myInfo = { ...nameOnly, ...lastNameOnly, ...emailOnly };

myInfo.name === nameOnly.name; // true
myInfo.email === nameOnly.email; // false
myInfo === nameOnly; // false
```

### Equality Operator (==) e Strict Equality Operador (===)

__Para tipos primitivos__

__==__ Compara igualdade de valores __(faz conversões)__

__===__ Compara igualdade de valores e tipo __(não faz conversões)__


```js
"1" == 1; // true
"1" === 1; // false
1 == true); // true
1 === true); // false
"" == false); // true
"" === false); // false
[] == false); // true
[] === false); // false
undefined == null); // true
undefined === null); // false
"" == undefined); // false
"" === undefined); // false
```

__Para tipos Object__

__==__ Compara referência (ignora estrutura e valores)

__===__ Compara referência (ignora estrutura e valores)

```js
var person = {
  name: 'Renato',
}

var person2 = {
    name: 'Renato',
}

var person3 = person;

person == person2; // false
person === person2; // false
person == person3; // true
person === person3; // true
```

__Bonus: Igualdade de NaN__
```js
const a = NaN;
const b = NaN;

a == NaN; // false
b == NaN; // false
a == b; // false
NaN == NaN; // false

Object.is(a, b); // true
Object.is(NaN, NaN); // true
```

[ECMAScript 5th Edition]: https://www.w3schools.com/js/js_es5.asp
[ECMAScript 6th Edition]: https://www.w3schools.com/js/js_es6.asp
[ECMAScript 9th Edition]: https://www.w3schools.com/js/js_2018.asp
[Underscore.js]: https://underscorejs.org/
[copy-on-write]: https://en.wikipedia.org/wiki/Copy-on-write
[Immer]: https://immerjs.github.io/immer/
[Redux-ecosystem-links]: https://github.com/markerikson/redux-ecosystem-links/blob/master/immutable-data.md#immutable-update-utilities
[Referência completa de mutação em objetos e arrays]: https://immerjs.github.io/immer/update-patterns

[demo-reducer-before]: https://codesandbox.io/s/usereducer-demo-e76k0?file=/src/Demo2/state/reducer.ts
[demo-reducer-after]: https://codesandbox.io/s/usereducer-demo-with-immer-ite7t?file=/src/Demo2/state/reducer.ts
[sse-reducer]: #private
[checklist-reducer]: #private