web-dev-qa-db-fra.com

React Crochets useState () avec Object

Quelle est la bonne façon de mettre à jour l'état, est un objet imbriqué, dans React with Hooks?

export Example = () => {
  const [exampleState, setExampleState] = useState(
  {masterField: {
        fieldOne: "a",
        fieldTwo: {
           fieldTwoOne: "b"
           fieldTwoTwo: "c"
           }
        }
   })

Comment utiliserait-on setExampleState pour mettre à jour exampleState vers a (en ajoutant un champ)?

const a = {
masterField: {
        fieldOne: "a",
        fieldTwo: {
           fieldTwoOne: "b",
           fieldTwoTwo: "c"
           }
        },
  masterField2: {
        fieldOne: "c",
        fieldTwo: {
           fieldTwoOne: "d",
           fieldTwoTwo: "e"
           }
        },
   }
}

b (Modification des valeurs)?

const b = {masterField: {
        fieldOne: "e",
        fieldTwo: {
           fieldTwoOne: "f"
           fieldTwoTwo: "g"
           }
        }
   })

51
isaacsultan

Vous pouvez transmettre une nouvelle valeur comme celle-ci

  setExampleState({...exampleState,  masterField2: {
        fieldOne: "c",
        fieldTwo: {
           fieldTwoOne: "d",
           fieldTwoTwo: "e"
           }
        },
   }})
52
aseferov

En règle générale, vous devez faire attention aux objets profondément imbriqués dans l'état React. Pour éviter un comportement inattendu, l'état doit être mis à jour de manière immuable. Lorsque vous avez des objets profonds, vous finissez par les cloner en profondeur pour l'immuabilité, ce qui peut être assez cher dans React. Pourquoi?

Une fois que vous avez cloné l'état en profondeur, React recalculera et restituera tout ce qui dépend des variables, même si elles n'ont pas changé!

Donc, avant d'essayer de résoudre votre problème, réfléchissez à la façon dont vous pouvez d'abord aplanir l'état. Dès que vous faites cela, vous trouverez de beaux outils qui vous aideront à gérer les grands états, tels que useReducer ().

Si vous y avez pensé, mais êtes toujours convaincu que vous devez utiliser un arbre d'état profondément imbriqué, vous pouvez toujours utiliser useState () avec des bibliothèques comme immutable.js et Immutability-helper . Ils facilitent la mise à jour ou le clonage d'objets profonds sans avoir à se soucier de la mutabilité.

21
Ilyas Assainov

Je vous laisse une fonction utilitaire pour mettre à jour les objets de façon inimitable

/**
 * Inmutable update object
 * @param  {Object} oldObject     Object to update
 * @param  {Object} updatedValues Object with new values
 * @return {Object}               New Object with updated values
 */
export const updateObject = (oldObject, updatedValues) => {
  return {
    ...oldObject,
    ...updatedValues
  };
};

Vous pouvez donc l'utiliser comme ceci

const MyComponent = props => {

  const [orderForm, setOrderForm] = useState({
    specialities: {
      elementType: "select",
      elementConfig: {
        options: [],
        label: "Specialities"
      },
      touched: false
    }
  });


// I want to update the options list, to fill a select element

  // ---------- Update with fetched elements ---------- //

  const updateSpecialitiesData = data => {
    // Inmutably update elementConfig object. i.e label field is not modified
    const updatedOptions = updateObject(
      orderForm[formElementKey]["elementConfig"],
      {
        options: data
      }
    );
    // Inmutably update the relevant element.
    const updatedFormElement = updateObject(orderForm[formElementKey], {
      touched: true,
      elementConfig: updatedOptions
    });
    // Inmutably update the relevant element in the state.
    const orderFormUpdated = updateObject(orderForm, {
      [formElementKey]: updatedFormElement
    });
    setOrderForm(orderFormUpdated);
  };

  useEffect(() => {
      // some code to fetch data
      updateSpecialitiesData.current("specialities",fetchedData);
  }, [updateSpecialitiesData]);

// More component code
}

Sinon, vous avez plus d'utilitaires ici: https://es.reactjs.org/docs/update.html

0
Gonzalo

C'est une solution ES6 que j'ai trouvée qui permet de mettre à jour n'importe quel objet imbriqué dans l'état:

export const updateStateObject = (object, keyToUpdate, newValue) => {

  let updatedObject = {};

  // Iterate over the object
  Object.entries(object).forEach(([key, value]) => {

    // Update the chosen key
    if (key === keyToUpdate) Object.assign(updatedObject, { [key]: newValue });

    // If the iterated key doesn't match the given key return the current value
    else if (key !== keyToUpdate) Object.assign(updatedObject, { [key]: value });
  });

  return updatedObject;
};

Ensuite, lorsque vous devez mettre à jour l'état, appelez simplement:

setState({
  ...state,
  key: updateStateObject(object, keyToUpdate, newValue)
})
0
Gabriele Rapone

Je suis en retard à la fête .. :)

La réponse @aseferov fonctionne très bien lorsque l'intention est de ressaisir la structure entière de l'objet. Cependant, si l'objectif/le but est de mettre à jour une valeur de champ spécifique dans un objet, je pense que l'approche ci-dessous est meilleure.

situation:

const [infoData, setInfoData] = useState({
    major: {
      name: "John Doe",
      age: "24",
      sex: "M",
    },

    minor:{
      id: 4,
      collegeRegion: "south",

    }

  });

La mise à jour d'un enregistrement spécifique nécessitera un rappel à l'état précédent prevState

Ici:

setInfoData((prevState) => ({
      ...prevState,
      major: {
        ...prevState.major,
        name: "Tan Long",
      }
    }));

peut-être

setInfoData((prevState) => ({
      ...prevState,
      major: {
        ...prevState.major,
        name: "Tan Long",
      },
      minor: {
        ...prevState.minor,
        collegeRegion: "northEast"

    }));

J'espère que cela aide toute personne essayant de résoudre un problème similaire.

0
Eman Yalpsid