簡體   English   中英

React:使用 useState 掛鈎更新數組時出現問題

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM