状態を更新する正しい方法は何ですか?React with Hooks?)
export Example = () => {
const [exampleState, setExampleState] = useState(
{masterField: {
fieldOne: "a",
fieldTwo: {
fieldTwoOne: "b"
fieldTwoTwo: "c"
}
}
})
setExampleState
をどのように使用してexampleState
をa
に更新しますか(フィールドの追加)?
const a = {
masterField: {
fieldOne: "a",
fieldTwo: {
fieldTwoOne: "b",
fieldTwoTwo: "c"
}
},
masterField2: {
fieldOne: "c",
fieldTwo: {
fieldTwoOne: "d",
fieldTwoTwo: "e"
}
},
}
}
b
(値の変更)?
const b = {masterField: {
fieldOne: "e",
fieldTwo: {
fieldTwoOne: "f"
fieldTwoTwo: "g"
}
}
})
このような新しい値を渡すことができます
setExampleState({...exampleState, masterField2: {
fieldOne: "c",
fieldTwo: {
fieldTwoOne: "d",
fieldTwoTwo: "e"
}
},
}})
一般に、React状態で深くネストされたオブジェクトに注意する必要があります。予期しない動作を回避するために、状態は不変に更新する必要があります。深いオブジェクトがある場合、不変性のためにそれらを深く複製することになります。 Reactではかなり高価になる可能性があります。
状態をディープクローンすると、Reactは、変数が変更されていなくても、変数に依存するすべてを再計算して再レンダリングします。
したがって、問題を解決する前に、まず状態を平坦化する方法を考えてください。それを行うとすぐに、useReducer()などの大きな状態の処理に役立つ美しいツールが見つかります。
考えたが、深くネストされた状態ツリーを使用する必要があると確信している場合は、useState()を immutable.js や Immutability-helper 。可変性を気にすることなく、ディープオブジェクトを簡単に更新または複製できます。
オブジェクトを不変に更新するユーティリティ関数を残します
/**
* 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
};
};
このように使えます
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
}
そうでない場合は、ここにさらにユーティリティがあります: https://es.reactjs.org/docs/update.html
それは私が思いついたES6ソリューションで、状態にネストされたオブジェクトを更新できます。
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;
};
次に、状態を更新する必要がある場合は、次を呼び出します。
setState({
...state,
key: updateStateObject(object, keyToUpdate, newValue)
})
私はパーティーに遅れました.. :)
オブジェクト構造全体を再入力することが目的の場合、@ aseferovの回答は非常にうまく機能します。ただし、ターゲット/目標がオブジェクトの特定のフィールド値を更新することである場合、以下のアプローチの方が優れていると思います。
状況:
const [infoData, setInfoData] = useState({
major: {
name: "John Doe",
age: "24",
sex: "M",
},
minor:{
id: 4,
collegeRegion: "south",
}
});
特定のレコードを更新するには、以前の状態に呼び戻す必要がありますprevState
ここに:
setInfoData((prevState) => ({
...prevState,
major: {
...prevState.major,
name: "Tan Long",
}
}));
たぶん
setInfoData((prevState) => ({
...prevState,
major: {
...prevState.major,
name: "Tan Long",
},
minor: {
...prevState.minor,
collegeRegion: "northEast"
}));
これが同様の問題を解決しようとしている人を助けることを願っています。