Inmutabilidad
Tags: javascript, webdev, react, typescript
Published: 2022-03-31 15:35:06

El concepto de inmutabilidad es muy importante dentro del entorno de React, ya que sin dominar
este concepto, podemos estar causando renderizados extra o efectos secundarios dentro de nuestra aplicación.

## ¿Qué es inmutabilidad?

Un objeto inmutable es aquel objeto que no puede ser modificado una vez creado, no puede cambiar su estructura ni los datos que contiene, este no es el caso de las constantes, porque cuando sea crea una constante, quizás no se puede reasignar el valor, pero si cambiar su estructura.

```ts
const case1 = {
  nem: "nem",
  nim: "nim",
};
case1.nim = "nom";
/*
{
   "nem": "nem",
   "nim": "nom"
}
*/
const case2 = {
  nem: "Hello",
};
// Cannot assign to 'case2' because it is a constant
case2 = case1;
```

Este ejemplo demuestra que las constantes puede ser modificadas, pero no pueden reasignarse.

Para entender que es inmutabilidad un poco más clara hay que echar un vistazo a como funcionan los objetos en JavaScript.


## Valores inmutables


Los valores inmutables o primitivos son aquellos que se almacenan completos en memoria, estos son `strings`, `numbers`, `booleans`, `null` y `undefined`. Es decir, estos tiene un lugar propio en memoria.

```ts
let case1 = "wenas";
let case2 = "wenas";
```
Por ejemplo estos dos variables son almacenadas de la siguiente manera:


![memory 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jzahnof7hhnjmf2xu1dh.png)

Entonces al ser objetos primitivos, su valor es el mismo

```ts
const areEqual = (a, b) => {
  return a === b;
};

const case1 = "wenas";
const case2 = "wenas";

console.log(areEqual(case1, case2));
//  true
```

## Valores mutables:

En el caso de los objetos no pasa lo mismo que con los primitivos, estos son almacenados de manera distinta.

```ts
const case3 = { name: "pepe", lastName: "perez" };
const case4 = { name: "pepe", lastName: "perez" };

console.log(areEqual(case3, case4));
// false
```
Eso es porque una variable asignada a un objeto, no es almacenado en el objeto en sí, este solo almacena su dirección de memoria.


![object reference](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dzq38rzuyqmjcwhn3a7k.png)

Se puede decir que un objeto es como una agenda con las direcciones
de memoria de otros objetos, entonces cuando hace algo con alguna de las propiedades como `case.name` el motor de JavaScript toma esa dirección de memoria y realiza la operación en el objeto real.

## Copiar un objeto:

Cuando copiamos un objeto, copiamos la referencia, Por ejemplo:
Supongamos que tenemos un objeto usuario y queremos copiarlo, para a partir de él, crear un usuario 2.

```ts
const user1 = { name: "pepe", lastName: "perez" };
const user2 = user1;
console.log(areEqual(user1, user2));
// true
```
En este caso podríamos decir que `user2` tiene la misma información a que `user1` y, por lo tanto, son iguales.

```ts
const user1 = { name: "pepe", lastName: "perez" };
const user2 = user1;
user2.name = "pepe2";
console.log(areEqual(user1, user2));
// true
```

Pero si alteramos la propiedad de user2, siguen siendo iguales, ¿Por qué?, bueno, eso es porque al hacer `const user2=user1` estamos creando una referencia a las mismas direcciones de memoria, y al afectar `user2.name` afecta a `user1.name` también.

```ts
const user1 = { name: "pepe", lastName: "perez" };
const user2 = user1;
user2.name = "pepe2";

console.log(areEqual(user1, user2));
// true
console.log({
  user1,
  user2,
});

/**
 *  {
 *   user1 : {
 *    name :"pepe2",
 *    lastName :"perez"
 *  },
 *   user2 : {
 *    name :"pepe2",
 *    lastName :"perez"
 *  }
 * }
 */
```

Esto se tiene que tener en cuenta a la hora de escribir código.
Porque podemos crear efectos secundarios sin querer. La manera efectiva de hacerlo sería la siguiente.

```ts
const user1 = { name: "pepe", lastName: "perez" };
const user2 = { ...user1 };
user2.name = "pepe2";
console.log(areEqual(user1, user2));
// false
```

En este caso, al utilizar [spreed operator](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Operators/Spread_syntax), para sacar toda la propiedad de `user1` y pasársela a `user2` el cual es un nuevo objeto(nueva direcciones de memoria). También podemos utilizar [object.assign](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)

## Inmutabilidad en React

Provocar efectos secundarios en React puede ser peligroso. En`React` para hacer la diferencia del estado anterior y el nuevo devuelto, hace igualdad estricta, porque, sería un impacto de performance hacer una igualación a profundidad. 

Por eso es que se prohíbe el uso de cualquier método que mute el estado, como en el caso de los array como [`Array.prototype.push`](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/push) porque mutan el array, a diferencia de [`Array.prototype.filter`] el cual devuelve uno nuevo.


> Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
> [docs](https://reactjs.org/docs/react-component.html#state)


Por ejemplo, hacer esto es incorrecto.

```ts
export default function App() {
  const [array, setArray] = React.useState([1, 2]);
  return (
    <button
      onClick={() => {
        array.push(3);
        setArray(array);
      }}
    ></button>
  );
}
```

la forma correcta de hacerlo sería la siguiente:

```ts
export default function App() {
  const [array, setArray] = React.useState([1, 2]);
  return (
    <button
      onClick={() => {
        setArray([...array, 4]);
      }}
    ></button>
  );
}
```

## Immer al rescate

Teniendo en cuenta la inmutabilidad y su importancia dentro de nuestras aplicaciones, en cierto punto puedes toparte con código así.

```ts
const state = {
  prop1: {
    prop11: "1",
    prop12: "2",
    prop13: {
      prop131: "1",
      prop132: {
        prop1321: "1",
      },
    },
  },
  prop2: {
    prop21: "2",
  },
};

// changue  prop1321 to "2"  :)

const nextState = {
  ...state,
  prop1: {
    ...state.prop1,
    prop13: {
      ...state.prop1.prop13,
      prop132: {
        ...state.prop1.prop13.prop132,
        prop1321: "2", // :(
      },
    },
  },
};
```

[Immer](https://immerjs.github.io/immer/) que hace que la inmutabilidad sea facil de hacer.

```ts
import produce from "immer";

const state = {
  prop1: {
    prop11: "1",
    prop12: "2",
    prop13: {
      prop131: "1",
      prop132: {
        prop1321: "1",
      },
    },
  },
  prop2: {
    prop21: "2",
  },
};

const nextState = produce(state, (draftState) => {
  draftState.prop1.prop13.prop132.prop1321 = "2"; //:)
});
```

Lo que hace immer es aplicar temporalmente los cambios a `drafState`el cual es un [proxy](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Proxy). Una vez que la mutación es completada,  immer se encarga de producir el siguiente estado, basado en las mutaciones a `draftState`.

De esta forma no tenemos que preocuparnos de afectar el estado, de hecho, ni de retornar el estado de borrador. Immer ofrce otras características, como [utilities para facilitar su uso](https://immerjs.github.io/immer/api) e [integración con react](https://immerjs.github.io/immer/example-setstate) con el paquete `use-immer`.


## Conclusiones

-  Declarar una variable como constante, evita que se reasigne más no que sea mutada.
-  Entender inmutabilidad puede ser fundamental en React, porque eso evita que se produzcan efectos secundarios.
-  Immer facilita la inmutabilidad encargándose de todo el proceso y encargándose de que el resultado sea el correcto.

Happy coding :)