Javascript 反应:使用useState钩子更新数组时出现问题

Javascript 反应:使用useState钩子更新数组时出现问题,javascript,reactjs,react-native,Javascript,Reactjs,React Native,我有一个表示企业营业时间的对象数组,现在用户可以更改特定日期和班次的开门和关门时间,添加新班次,也可以删除新班次,我仍然是react和state hook的新手,我的更改没有反映在我的视图中 我正在尝试的行动是: 正在更新数组中的特定项属性 openingTimes[dayIndex].hours[splitIndex].opens = selectedHour+':'+selectedMinute; 删除数组的最后一项 openingTimes[i].hours.pop(); 向数组中添加

我有一个表示企业营业时间的对象数组,现在用户可以更改特定日期和班次的开门和关门时间,添加新班次,也可以删除新班次,我仍然是react和state hook的新手,我的更改没有反映在我的视图中

我正在尝试的行动是:

正在更新数组中的特定项属性

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对象。实际上,您必须创建状态的副本,然后对副本进行变异,然后使用新副本调用
setState

但是React的
setState
希望您在不更改或改变原始版本的情况下提供状态的新版本。这意味着每个包含更改的数组或对象都需要进行浅层复制,从上到下一直复制到进行更改的位置。对于像您这样的几个级别的深度状态,最终在香草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.
});
幸运的是,有一个名为npm的包,它在简化方面做得相当好。(不容易,但更容易)。使用immutability helper,您的突变将如下所示:

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' } } } }));
这无疑是朝着正确的方向迈出的一步,但所有这些嵌套对象意味着您需要平衡很多大括号。您可以使用的$functions选项有限。(例如,您会注意到我们不得不使用他们的
$slice
,因为他们没有
$pop

作为immutability helper的附加组件,我经常使用这个小函数来简化事情,因为immutability helper中的所有嵌套对象都会得到一些繁琐的IMHO。使用这个“update2”助手,您可以为“path”提供字符串或数组,它将为您构建嵌套对象结构

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' }));

令人困惑的是,您的状态被称为
openingTimes
,但也反映了关闭时间。imho说,将一个对象用于您的状态,并将天作为键将更容易使用。为关闭时间创建一个单独的状态,我敢打赌你一定能解决这个问题。我正在考虑将其重命名为businessHours,但在我看来,这并不令人困惑,我的问题不在于命名,而在于react状态挂钩对数组的工作方式,这是我的vue背景造成的困惑。数组在挂钩中的工作方式没有什么特别之处。如果您正在努力更新状态,请添加您正在进行的
setOpeningTimes
调用,这些调用不会给您预期的结果以及预期的结果。