Espero que alguien pueda ayudarme con una forma más fácil de actualizar mis estados de retroceso en objetos / matrices más complejos. Soy principalmente un desarrollador de C #, pero trato de aprender algunas formas decentes de codificar javascript. Esto simplemente parece feo y demasiado complicado, la forma en que se ve mi código actualmente.

Como la instancia de estado es de solo lectura, no puedo cambiar los valores directamente. El uso del método de clonación de guiones bajos ni siquiera cambia eso.

Así que aquí están mis objetos simplificados, en la vida real tienen muchas propiedades no relevantes:

interface IDeviceAttributeValue {
  /** The unique value key
  id: string;
  /** The attribute value */
  value: any;
}

interface IDeviceAttribute {
  /** A unique key to identify the attribute. Normally the OSC address of the setting is used */
  key: string;
  /** The list of attribute values */
  values: IDeviceAttributeValue[];
}

En React tengo la declaración estatal const [attribute, setAttribute] = useState(props.attribute as IDeviceAttribute);

O en algún otro lugar un estado de retroceso: const [deviceAttributeState, setDeviceAttributeState] = useRecoilState(recoilDeviceAttributeState);

Y en algún lugar del código necesito cambiar un valor en la matriz de valores y actualizar el estado. En ambos casos, con el estado React y el estado Recoil, la instancia 'getter' es readonly / const.

Termino con esto:

... code calculating a new value for existing value in editedSetting: IDeviceAttributeValue
...

// Now update state, first find the element in the array
let index = attribute.values.findIndex(l => l.id === editedSetting.id);
if (index !== -1) {
  let newValueItem = {
     ...attribute.values[index],
     value: newValue
  }
  setAttribute({
    ...attribute, 
    values: [...attribute.values.slice(0,index - 1), newValueItem, 
    ...attribute.values.slice(index + 1)]
  })
}

¡Tantas líneas de código para una simple actualización de estado! Estoy seguro de que para alguien esta es una tarea muy trivial y se puede hacer mucho más elegante :-)

Gracias por la ayuda y el tiempo

-1
Peter Stjernholm Meldgaard 23 feb. 2021 a las 17:41

2 respuestas

La mejor respuesta

Ok, parece que no hay forma de actualizar los estados basados ​​en objetos / matrices sin usar el operador de propagación y ocuparse de la actualización profunda, ya que la actualización de la propiedad superficial solo funciona en el nivel superior.

Esto significa que debe ocuparse de proporcionar los valores de propiedad existentes del estado actual y establecer los que desea cambiar en paralelo, y esto debe hacer en cada nivel de anidamiento.

Encontré una buena q / a con ejemplos aquí: blog

Entonces, en mi caso, terminé con un código como este para actualizar un objeto con una propiedad de matriz (configuración) con un nuevo valor para un elemento específico en la matriz:

setEditedDeviceState(curVal => ({
   ...curVal,
   settings: curVal.settings.map(
     el => el.key === attribute.key ? attribute : el
   )
}));

Debo administrar que encuentro que esto es un fastidio en el ... y un candidato para introducir fácilmente errores en sus modelos de datos. Si esto es una falta en el lenguaje en sí o la implementación de los estados react (retroceso, redux o lo que sea) es probablemente algo que se pueda discutir más a fondo. Pero parece que esto es con lo que tienes que vivir actualmente.

0
Peter Stjernholm Meldgaard 1 mar. 2021 a las 09:45

Si esto es frecuente en su código, puede extraer su lógica de actualización a un enlace personalizado. Estaba pensando en algo como

function useDeviceAttribute(initialValue: IDeviceAttribute) {
   const [attribute, setAttribute] = useState(initialValue);
   
   const updateAtId = (id: number, newValue: any) => {
      let index = attribute.values.findIndex(l => l.id === id);
      if (index !== -1) {
        let newValueItem = {
           ...attribute.values[index],
           value: newValue
        }
        setAttribute({
          ...attribute, 
          values: [...attribute.values.slice(0,index - 1), newValueItem, 
          ...attribute.values.slice(index + 1)]
        })
      }
   };

   return [attribute, updateAtId];
}

Entonces su código de componente sería algo como

function YourComponent(props) {
   const [attribute, updateAtId] = useDeviceAttribute(props.attribute);
   //
   // your component code goes here
   //
   // ... code calculating a new value for existing value in editedSetting: IDeviceAttributeValue
   /// ...
   // Now update state, first find the element in the array
   updateAtId(editedSetting.id, newValue);
}
0
Zoltan Magyar 24 feb. 2021 a las 08:39