[英]React: issues updating array with useState hook
我有一組代表企業營業時間的對象,現在用戶可以更改特定日期和班次的營業和關閉時間,添加新班次並刪除它們,我仍然是 react 和 state 的新手與 arrays 掛鈎,我的更改並未反映在我的觀點中。
我正在嘗試執行的操作是:
更新數組中的特定項目屬性
openingTimes[dayIndex].hours[splitIndex].opens = selectedHour+':'+selectedMinute;
刪除數組的最后一項
openingTimes[i].hours.pop();
將項目添加到數組
openingTimes[i].hours.push({ opens: 'Abre', closes: 'Cierra' });
以及供參考的對象的 openingTimes 數組:
const [openingTimes, setOpeningTimes] = useState([
{day:'monday', hours: [ { opens: 'Abre', closes: 'Cierra' } ]},
{day:'tuesday', hours: [ { opens: 'Abre', closes: 'Cierra' } ]},
{day:'wednesday', hours: [ { opens: 'Abre', closes: 'Cierra' } ]},
{day:'thursday', hours: [ { opens: 'Abre', closes: 'Cierra' } ]},
{day:'friday', hours: [ { opens: 'Abre', closes: 'Cierra' } ]},
{day:'saturday', hours: [ { opens: 'Abre', closes: 'Cierra' } ]},
{day:'sunday', hours: [ { opens: 'Abre', closes: 'Cierra' } ]},
]);
由於不變性問題,在復雜數據結構上更新 state 會變得有點復雜。 正如您所發現的,您不能直接更改 state object。 您實際上必須創建 state 的副本,然后改變您的副本,然后使用新副本調用您的setState
。
但是 React 的setState
希望你提供 state 的這個新版本,而不改變或改變原始版本。 這意味着每個包含更改的數組或 object 都需要進行淺拷貝,從上到下一直到您進行更改的位置。 對於像你這樣的幾級深度 state,這最終在香草 JavaScript 中看起來像這樣:
// openingTimes[dayIndex].hours[splitIndex].opens = selectedHour+':'+selectedMinute;
// every object/array containing a change needs to be "cloned" to make it a new object/array.
setOpeningTimes(x => {
const newState = [...x]; // clone array
let nextLevel = x[dayIndex];
newState[dayIndex] = { ...nextLevel }; // clone object
nextLevel = nextLevel.hours;
newState[dayIndex].hours = [...nextlevel]; // clone array
newState[dayIndex].hours.opens = selectedHour + ':' + selectedMinute; // change string value
return newState; // return iteratively cloned new state.
});
幸運的是,有一個名為 immutability-helper的 npm package 可以很好地簡化它。 (不容易,但更容易)。 使用不變性助手,您的突變將如下所示:
import update from 'immutability-helper';
// ...
// openingTimes[dayIndex].hours[splitIndex].opens = selectedHour+':'+selectedMinute;
setOpeningTimes(x => update(x, {
[dayIndex]: {
hours: {
[splitIndex]: {
$set: selectedHour + ':' + selectedMinute
}
}
}
}));
// openingTimes[i].hours.pop();
setOpeningTimes(x => update(x, { [i]: { hours: { $splice: [[x[i].hours.length-1, 1]] } } }));
// openingTimes[i].hours.push({ opens: 'Abre', closes: 'Cierra' });
setOpeningTimes(x => update(x, { [i]: { hours: { $push: { opens: 'Abre', closes: 'Cierra' } } } }));
絕對是朝着正確方向邁出的一步,但是所有這些嵌套對象意味着您有很多大括號需要平衡。 您可以使用有限的 $ 函數。 (例如,您會注意到我們必須使用他們的$slice
,因為他們沒有$pop
。)
作為 immutability-helper 的附加組件,我經常使用這個小的 function 來簡化事情,因為 immutability-helper 中的所有嵌套對象都會變得有點乏味恕我直言。 使用這個“update2”助手,你可以為你的“路徑”提供一個字符串或一個數組,它會為你構建嵌套的 object 結構。
import update from 'immutability-helper';
// ...
const update2 = (state, path, value) => update(state, toObj(path, value));
function toObj(arr, value) {
const obj = {}; let o = obj;
if (typeof arr === 'string') arr = arr.split('.');
const last = arr.pop();
while (arr.length) {
const key = arr.shift();
if (!o[key]) o[key] = {};
o = o[key];
}
o[last] = value;
return obj;
}
這就是它的做法。 我將在前兩條路徑中使用一個數組,然后在最后一條路徑中使用字符串表示法。
// openingTimes[dayIndex].hours[splitIndex].opens = selectedHour+':'+selectedMinute;
setOpeningTimes(x => update2(x, [dayIndex, 'hours' splitIndex, 'opens', '$set'], selectedHour+':'+selectedMinute));
// openingTimes[i].hours.pop();
setOpeningTimes(x => update2(x, [i, 'hours', '$splice'], [[x[i].hours.length-1, 1]]));
// openingTimes[i].hours.push({ opens: 'Abre', closes: 'Cierra' });
setOpeningTimes(x => update2(x, `${i}.hours.$push`, { opens: 'Abre', closes: 'Cierra' }));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.