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"
}
}
})
Vous pouvez transmettre une nouvelle valeur comme celle-ci
setExampleState({...exampleState, masterField2: {
fieldOne: "c",
fieldTwo: {
fieldTwoOne: "d",
fieldTwoTwo: "e"
}
},
}})
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é.
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
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)
})
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.