es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Cómo puedo evitar renderizados innecesarios al almacenar un estado complejo en variables reactivas de Apollo?

Las variables reactivas tienen sentido cuando se almacenan valores simples y escalares, pero si necesitas lidiar con un estado más complejo (por ejemplo, un mapa de ID->objeto) te enfrentarás a problemas de rendimiento al renderizar. El problema es que las variables reactivas son efectivamente inmutables: no puedes simplemente mutar una subpropiedad de la variable y esperar que reaccione correctamente; debes proporcionar un valor completamente nuevo para la variable. Pero luego, cada componente que está escuchando esa variable se volverá a renderizar, aunque muchos de ellos puedan no preocuparse por esa propiedad en particular.

Lo aclararé con un ejemplo. Digamos que tengo esta variable reactiva:

const itemsMapVar = makeVar({
    item1: { text: "Texto 1" },
    item2: { text: "Texto 2" }
})

Y renderizo una lista de elementos, cada uno de los cuales se ve así:

const ListItem = (props) => {
    const item = useReactiveVar(itemsMapVar)[props.id];
    return <div>{item.text}</div>;
}

Supongamos que modifico el texto para el elemento 1:

itemsMapVar({
    ...itemsMapVar(),
    item1: { text: 'Nuevo texto' }
})

Ahora tanto el elemento 1 como el elemento 2 se volverán a renderizar, porque ambos están escuchando a itemsMapVar. Eso puede no ser un problema en este ejemplo trivial, pero si hay una lista larga de elementos con mucho contenido de DOM, será un problema. ¿Existe una mejor práctica establecida para lidiar con esto?

Algunas opciones que he considerado:
– Usar una biblioteca de estado diferente en el cliente. Algo como Redux o MobX resuelve este tipo de problema fácilmente. Una gran desventaja es que no puedo usar ese estado como parte de una consulta GraphQL reactiva, que probablemente sea por qué estoy usando Apollo en primer lugar.
– Colocar un componente envolvente alrededor de <listitem> que contenga el gancho useReactiveVar y extraiga la propiedad en la que estoy interesado. Luego, un React.memo en ListItem evitará que se vuelva a renderizar innecesariamente. Esto no es terrible, pero aún así se renderizarán elementos O(N), pero serán solo elementos envolventes livianos. Y se siente un poco verboso y torpe para lo que parece ser un problema común al tratar con el estado del cliente.
– Inventar una nueva versión de useReactiveVar que sepa cómo seleccionar la parte del estado que necesitas, similar a useSelector de Redux. (¿Ya existe algo así?)

Tags:  ,

Answer

  1. Avatar for davy.ai

    Manejo del Estado Complejo y Variables Reactivas en React/Apollo

    Las variables reactivas son una excelente manera de administrar valores escalares en aplicaciones de React. Sin embargo, cuando se trata de un estado complejo, como un mapa de ID->objeto, las variables reactivas pueden no ser la mejor opción. El problema con las variables reactivas es que son efectivamente inmutables, lo que significa que mutar alguna subpropiedad de la variable requeriría suministrar un valor completamente nuevo para la variable. Esto puede llevar a problemas de rendimiento cuando cada componente que escucha esa variable se vuelve a renderizar, incluso si muchos de esos componentes pueden no estar interesados en esa propiedad en particular.

    Por ejemplo, si tenemos una variable reactiva que almacena un mapa de ID->objeto:

    const itemsMapVar = makeVar({
      item1: { text: "Texto 1" },
      item2: { text: "Texto 2" },
    });
    

    Y renderizamos una lista de elementos, así:

    const ListItem = (props) => {
      const item = useReactiveVar(itemsMapVar)[props.id];
      return <div>{item.text}</div>;
    };
    

    Si modificamos el texto para el elemento 1:

    itemsMapVar({
      ...itemsMapVar(),
      item1: { text: 'Nuevo texto' }
    })
    

    Tanto item1 como item2 se volverán a renderizar, porque ambos están escuchando itemsMapVar. Si hay una lista larga de elementos pesados en el DOM, esto será un problema.

    Para resolver este problema, aquí tienes algunas opciones:

    • Utilizar una biblioteca de estado del cliente diferente, como Redux o MobX, que resuelva este tipo de problemas fácilmente. Una gran desventaja es que no puedes utilizar ese estado como parte de una consulta GraphQL reactiva, que probablemente sea la razón por la que estás utilizando Apollo en primer lugar.
    • Colocar un componente contenedor alrededor de ListItem que contenga el hook useReactiveVar y extraiga la propiedad que te interesa. Luego, un React.memo en ListItem evitará que se renderice innecesariamente. Este enfoque seguirá renderizando elementos O(N), pero estos serán envoltorios más livianos y pequeños. También parece un poco verboso y torpe para lo que parece ser un problema común al tratar con el estado del cliente.
    • Inventar alguna nueva versión de useReactiveVar que sepa cómo seleccionar la parte del estado que necesitas, similar a useSelector de Redux. La ventaja de este enfoque es que puedes evitar renderizaciones innecesarias seleccionando solo el estado que necesitas. Sin embargo, dicha implementación tendría que encargarse de manejar la inmutabilidad de la variable reactiva, lo que lo convierte en una propuesta desafiante.

Comments are closed.