Mejores reductores con Immer

 

 

 

  • SmashingConf Friburgo 2024

  • Índice
    1. Inmutabilidad en JavaScript y por qué es importante
    2. produceFunción de Immer
    3. Reductores de escritura con Immer.
    4. Usando los ganchos useImmerYuseImmerReducer
    5. Por qué debería utilizar Immer
    6. Conclusión
      1. Recursos Relacionados

    En este artículo, aprenderemos cómo usar Immer para escribir reductores. Cuando trabajamos con React, mantenemos mucho estado. Para realizar actualizaciones en nuestro estado, necesitamos escribir muchos reductores. Escribir reductores manualmente da como resultado un código inflado donde tenemos que tocar casi todas las partes de nuestro estado. Esto es tedioso y propenso a errores. En este artículo, veremos cómo Immer aporta más simplicidad al proceso de escritura de reductores de estado.

     

    Como desarrollador de React, ya debería estar familiarizado con el principio de que el estado no debe mutarse directamente. Quizás se pregunte qué significa eso (la mayoría de nosotros teníamos esa confusión cuando empezamos).

    Este tutorial hará justicia a eso: comprenderá qué es el estado inmutable y su necesidad. También aprenderá cómo usar Immer para trabajar con estados inmutables y los beneficios de usarlo. Puede encontrar el código en este artículo en este repositorio de Github .

    Inmutabilidad en JavaScript y por qué es importante

    Immer.js es una pequeña biblioteca de JavaScript escrita por Michel Weststrate cuya misión declarada es permitirle "trabajar con estados inmutables de una manera más conveniente".

    Pero antes de sumergirnos en Immer, repasemos rápidamente la inmutabilidad en JavaScript y por qué es importante en una aplicación React.

    El último estándar ECMAScript (también conocido como JavaScript) define nueve tipos de datos integrados. De estos nueve tipos, hay seis a los que se hace referencia como primitivevalores/tipos. Estas seis primitivas son undefined, number, string, boolean, biginty symbol. Una simple verificación con typeofel operador de JavaScript revelará los tipos de estos tipos de datos.

    console.log(typeof 5) // numberconsole.log(typeof 'name') // stringconsole.log(typeof (1 2)) // booleanconsole.log(typeof undefined) // undefinedconsole.log(typeof Symbol('js')) // symbolconsole.log(typeof BigInt(900719925474)) // bigint

    A primitivees un valor que no es un objeto y no tiene métodos. Lo más importante para nuestra discusión actual es el hecho de que el valor de una primitiva no se puede cambiar una vez creada. Por tanto, se dice que los primitivos son immutable.

     

    Los tres tipos restantes son null, objecty function. También podemos comprobar sus tipos utilizando el typeofoperador.

    console.log(typeof null) // objectconsole.log(typeof [0, 1]) // objectconsole.log(typeof {name: 'name'}) // objectconst f = () = ({})console.log(typeof f) // function

    Estos tipos son mutable. Esto significa que sus valores se pueden cambiar en cualquier momento después de su creación.

    Quizás se pregunte por qué tengo la matriz [0, 1]ahí arriba. Bueno, en JavaScriptlandia, una matriz es simplemente un tipo especial de objeto. En caso de que también te estés preguntando en nullqué se diferencia de undefined. undefinedsimplemente significa que no hemos establecido un valor para una variable, mientras que nulles un caso especial para los objetos. Si sabes que algo debería ser un objeto pero el objeto no está ahí, simplemente devuelves null.

    Para ilustrarlo con un ejemplo simple, intente ejecutar el siguiente código en la consola de su navegador.

    console.log('aeiou'.match(/[x]/gi)) // nullconsole.log('xyzabc'.match(/[x]/gi)) // [ 'x' ]

    String.prototype.matchdebería devolver una matriz, que es un objecttipo. Cuando no puede encontrar dicho objeto, devuelve null. Regresar undefinedaquí tampoco tendría sentido.

    Ya basta con eso. Volvamos a discutir la inmutabilidad.

    Según los documentos de MDN:

    "Todos los tipos, excepto los objetos, definen valores inmutables (es decir, valores que no se pueden cambiar)".

    Esta declaración incluye funciones porque son un tipo especial de objeto JavaScript. Vea la definición de función aquí .

    Echemos un vistazo rápido a lo que significan en la práctica los tipos de datos mutables e inmutables. Intente ejecutar el siguiente código en la consola de su navegador.

    let a = 5;let b = aconsole.log(`a: ${a}; b: ${b}`) // a: 5; b: 5b = 7console.log(`a: ${a}; b: ${b}`) // a: 5; b: 7

    Nuestros resultados muestran que aunque bse "deriva" de a, cambiar el valor de bno afecta el valor de a. Esto surge del hecho de que cuando el motor JavaScript ejecuta la declaración b = a, crea una nueva ubicación de memoria separada, la coloca 5allí y apunta ba esa ubicación.

    ¿Qué pasa con los objetos? Considere el siguiente código.

    let c = { name: 'some name'}let d = c;console.log(`c: ${JSON.stringify(c)}; d: ${JSON.stringify(d)}`) // {"name":"some name"}; d: {"name":"some name"}d.name = 'new name'console.log(`c: ${JSON.stringify(c)}; d: ${JSON.stringify(d)}`) // {"name":"new name"}; d: {"name":"new name"}

    Podemos ver que cambiar la propiedad del nombre mediante variable dtambién la cambia en c. Esto surge del hecho de que cuando el motor de JavaScript ejecuta la declaración, c = { name: 'some name' }el motor de JavaScript crea un espacio en la memoria, coloca el objeto dentro y clo señala. Luego, cuando ejecuta la declaración d = c, el motor JavaScript simplemente apunta da la misma ubicación. No crea una nueva ubicación de memoria. Por lo tanto, cualquier cambio en los elementos de des implícitamente una operación en los elementos de c. Sin mucho esfuerzo, podemos ver por qué esto es un problema en ciernes.

     

    Imagine que está desarrollando una aplicación React y en algún lugar desea mostrar el nombre del usuario some nameleyendo desde la variable c. Pero en algún otro lugar introdujiste un error en tu código al manipular el objeto d. Esto daría como resultado que el nombre del usuario apareciera como new name. Si cfuéramos dprimitivos no tendríamos ese problema. Pero las primitivas son demasiado simples para los tipos de estado que debe mantener una aplicación React típica.

    Se trata de las principales razones por las que es importante mantener un estado inmutable en su aplicación. Le animo a que consulte algunas otras consideraciones leyendo esta breve sección del README de Immutable.js: el caso de la inmutabilidad .

    Habiendo entendido por qué necesitamos la inmutabilidad en una aplicación React, echemos un vistazo a cómo Immer aborda el problema con su producefunción.

    produceFunción de Immer

    La API principal de Immer es muy pequeña y la función principal con la que trabajará es la producefunción. producesimplemente toma un estado inicial y una devolución de llamada que define cómo se debe mutar el estado. La devolución de llamada en sí recibe una copia borrador (idéntica, pero aún una copia) del estado en el que realiza toda la actualización prevista. Finalmente, es produceun estado nuevo e inmutable con todos los cambios aplicados.

    El patrón general para este tipo de actualización de estado es:

    // produce signatureproduce(state, callback) = nextState

    Veamos cómo funciona esto en la práctica.

    import produce from 'immer'const initState = { pets: ['dog', 'cat'], packages: [ { name: 'react', installed: true }, { name: 'redux', installed: true }, ],}// to add a new packageconst newPackage = { name: 'immer', installed: false }const nextState = produce(initState, draft = { draft.packages.push(newPackage)})

    En el código anterior, simplemente pasamos el estado inicial y una devolución de llamada que especifica cómo queremos que ocurran las mutaciones. Es tan simple como eso. No necesitamos tocar ninguna otra parte del estado. Deja initStateintactas y comparte estructuralmente aquellas partes del estado que no tocamos entre el estado inicial y el nuevo. Una de esas partes en nuestro estado es la petsmatriz. La produced nextStatees un árbol de estado inmutable que tiene los cambios que hemos realizado y las partes que no modificamos.

     

    Armados con este conocimiento simple pero útil, echemos un vistazo a cómo producepuede ayudarnos a simplificar nuestros reductores de React.

    Reductores de escritura con Immer.

    Supongamos que tenemos el objeto de estado definido a continuación.

    const initState = { pets: ['dog', 'cat'], packages: [ { name: 'react', installed: true }, { name: 'redux', installed: true }, ],};

    Y queríamos agregar un nuevo objeto y, en un paso posterior, establecer su installedclave entrue Diets, plans and health

    const newPackage = { name: 'immer', installed: false };

    Si tuviéramos que hacer esto de la forma habitual con la sintaxis de distribución de matrices y objetos de JavaScript, nuestro reductor de estado podría verse como se muestra a continuación.

    const updateReducer = (state = initState, action) = { switch (action.type) { case 'ADD_PACKAGE': return { ...state, packages: [...state.packages, action.package], }; case 'UPDATE_INSTALLED': return { ...state, packages: state.packages.map(pack = pack.name === action.name ? { ...pack, installed: action.installed } : pack ), }; default: return state; }};

    Podemos ver que esto es innecesariamente detallado y propenso a errores para este objeto de estado relativamente simple. También tenemos que tocar cada parte del estado, lo cual es innecesario. Veamos cómo podemos simplificar esto con Immer.

    const updateReducerWithProduce = (state = initState, action) = produce(state, draft = { switch (action.type) { case 'ADD_PACKAGE': draft.packages.push(action.package); break; case 'UPDATE_INSTALLED': { const package = draft.packages.filter(p = p.name === action.name)[0]; if (package) package.installed = action.installed; break; } default: break; } });

    Y con unas pocas líneas de código, hemos simplificado enormemente nuestro reductor. Además, si caemos en el caso predeterminado, Immer simplemente devuelve el estado de borrador sin que tengamos que hacer nada. Observe cómo hay menos código repetitivo y la eliminación de la difusión estatal. Con Immer sólo nos preocupamos de la parte del estado que queremos actualizar. Si no podemos encontrar dicho elemento, como en la acción `UPDATE_INSTALLED`, simplemente avanzamos sin tocar nada más. La función "producir" también se presta al curry. Pasar una devolución de llamada como primer argumento para "producir" está destinado a usarse para curry. La firma del "producto" al curry es

    //curried produce signatureproduce(callback) = (state) = nextState

    Veamos cómo podemos actualizar nuestro estado anterior con un producto al curry. Nuestros productos al curry se verían así:

    const curriedProduce = produce((draft, action) = { switch (action.type) { case 'ADD_PACKAGE': draft.packages.push(action.package); break; case 'SET_INSTALLED': { const package = draft.packages.filter(p = p.name === action.name)[0]; if (package) package.installed = action.installed; break; } default: break; }});

    La función de producto al curry acepta una función como primer argumento y devuelve un producto al curry que ahora requiere un estado a partir del cual producir el siguiente estado. El primer argumento de la función es el estado del borrador (que se derivará del estado que se pasará al llamar a este producto con curry). Luego sigue cada número de argumentos que deseamos pasar a la función.

     

    Todo lo que necesitamos hacer ahora para usar esta función es pasar el estado desde el cual queremos producir el siguiente estado y el objeto de acción así.

    // add a new package to the starting stateconst nextState = curriedProduce(initState, { type: 'ADD_PACKAGE', package: newPackage,});// update an item in the recently produced stateconst nextState2 = curriedProduce(nextState, { type: 'SET_INSTALLED', name: 'immer', installed: true,});

    Tenga en cuenta que en una aplicación React, cuando usamos el useReducergancho, no necesitamos pasar el estado explícitamente como lo hice anteriormente porque se encarga de eso.

    Quizás se pregunte: ¿Immer obtendría un hook, como todo en React en estos días? Bueno, estás en compañía de buenas noticias. Immer tiene dos ganchos para trabajar con el estado: el useImmery los useImmerReducerganchos. Veamos cómo funcionan.

    Usando los ganchos useImmerYuseImmerReducer

    La mejor descripción del useImmergancho proviene del propio archivo README de use-immer.

    useImmer(initialState)es muy similar a useState. La función devuelve una tupla, el primer valor de la tupla es el estado actual, el segundo es la función de actualización, que acepta una función de productor inmerso , en la que se draftpuede mutar libremente, hasta que el productor finalice y los cambios se vuelvan inmutables. y convertirse en el próximo estado.

    Para hacer uso de estos ganchos, hay que instalarlos por separado, además de la biblioteca principal de Immer.

    yarn add immer use-immer

    En términos de código, el useImmergancho se parece a continuación

    import React from "react";import { useImmer } from "use-immer";const initState = {}const [ data, updateData ] = useImmer(initState)

    Y es tan simple como eso. Se podría decir que es el estado de uso de React pero con un poco de esteroide. Utilizar la función de actualización es muy sencillo. Recibe el estado de borrador y puede modificarlo tanto como desee, como se muestra a continuación.

    // make changes to dataupdateData(draft = { // modify the draft as much as you want.})

    El creador de Immer ha proporcionado un ejemplo de codesandbox con el que puedes jugar para ver cómo funciona.

    useImmerReduceres igualmente simple de usar si ha usado useReducerel gancho de React. Tiene una firma similar. Veamos cómo se ve eso en términos de código.

    import React from "react";import { useImmerReducer } from "use-immer";const initState = {}const reducer = (draft, action) = { switch(action.type) { default: break; }}const [data, dataDispatch] = useImmerReducer(reducer, initState);

    Podemos ver que el reductor recibe un draftestado que podemos modificar tanto como queramos. También hay un ejemplo de codesandbox aquí para que experimentes.

     

    Y así de sencillo es utilizar los ganchos Immer. Pero en caso de que todavía te preguntes por qué deberías usar Immer en tu proyecto, aquí tienes un resumen de algunas de las razones más importantes que he encontrado para usar Immer.

    Por qué debería utilizar Immer

    Si ha escrito lógica de gestión de estado durante algún tiempo, apreciará rápidamente la simplicidad que ofrece Immer. Pero ese no es el único beneficio que ofrece Immer.

    Cuando usas Immer, terminas escribiendo menos código repetitivo como hemos visto con reductores relativamente simples. Esto también hace que las actualizaciones profundas sean relativamente fáciles.

    Con bibliotecas como Immutable.js , debes aprender una nueva API para aprovechar los beneficios de la inmutabilidad. Pero con Immer logras lo mismo con JavaScript normal Objects, Arrays, Setsy Maps. No hay nada nuevo que aprender.

    Immer también proporciona uso compartido estructural de forma predeterminada. Esto simplemente significa que cuando realiza cambios en un objeto de estado, Immer comparte automáticamente las partes no modificadas del estado entre el nuevo estado y el estado anterior.

    Con Immer, también obtienes la congelación automática de objetos, lo que significa que no puedes realizar cambios en el producedestado. Por ejemplo, cuando comencé a usar Immer, intenté aplicar el sortmétodo en una matriz de objetos devueltos por la función producir de Immer. Arrojó un error diciéndome que no puedo realizar ningún cambio en la matriz. Tuve que aplicar el método de corte de matriz antes de aplicar sort. Una vez más, lo producido nextStatees un árbol de estados inmutable.

    Immer también está fuertemente tipado y es muy pequeño, con solo 3 KB cuando está comprimido con gzip.

    Conclusión

    Cuando se trata de administrar actualizaciones de estado, usar Immer es una obviedad para mí. Es una biblioteca muy liviana que te permite seguir usando todo lo que has aprendido sobre JavaScript sin intentar aprender algo completamente nuevo. Te animo a que lo instales en tu proyecto y comiences a usarlo de inmediato. Puede agregarlo y usarlo en proyectos existentes y actualizar incrementalmente sus reductores.

    También le recomiendo que lea la entrada introductoria del blog de Immer escrita por Michael Weststrate. La parte que encuentro especialmente interesante es "¿Cómo funciona Immer?" sección que explica cómo Immer aprovecha las características del lenguaje, como los servidores proxy y conceptos como copia en escritura .

    También te animo a que eches un vistazo a esta publicación de blog: Inmutabilidad en JavaScript: una visión contraria donde el autor, Steven de Salas, presenta sus pensamientos sobre los méritos de buscar la inmutabilidad.

    Espero que con lo que has aprendido en esta publicación puedas comenzar a usar Immer de inmediato.

    Recursos Relacionados

    1. use-immer, GitHub
    2. Inmersión , GitHub
    3. function, documentos web de MDN, Mozilla
    4. proxy, documentos web de MDN, Mozilla
    5. Objeto (informática) , Wikipedia
    6. " Inmutabilidad en JS ", Orji Chidi Matthew, GitHub
    7. “ Tipos y valores de datos de ECMAScript ”, Ecma International
    8. Colecciones inmutables para JavaScript , Immutable.js, GitHub
    9. “ El caso de la inmutabilidad ”, Immutable.js, GitHub

    (ks, ra, il)Explora más en

    • Herramientas
    • Reaccionar
    • redux
    • javascript





    Tal vez te puede interesar:

    1. ¿Deberían abrirse los enlaces en ventanas nuevas?
    2. 24 excelentes tutoriales de AJAX
    3. 70 técnicas nuevas y útiles de AJAX y JavaScript
    4. Más de 45 excelentes recursos y repositorios de fragmentos de código

    Mejores reductores con Immer

    Mejores reductores con Immer

    SmashingConf Friburgo 2024 Índice Inmutabilidad en JavaScript y por qué es importante

    programar

    es

    https://aprendeprogramando.es/static/images/programar-mejores-reductores-con-immer-1038-0.jpg

    2024-05-21

     

    Mejores reductores con Immer
    Mejores reductores con Immer

    Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

     

     

    Top 20