[英]React: issues updating array with useState hook
I have an array of objects representing the opening hours of a business, now the user is able to change the open and closing time of specific days and shifts, adding new shifts and removing them too, I'm still a newb at react and state hook with arrays and my changes are not reflected in my views.我有一组代表企业营业时间的对象,现在用户可以更改特定日期和班次的营业和关闭时间,添加新班次并删除它们,我仍然是 react 和 state 的新手与 arrays 挂钩,我的更改并未反映在我的观点中。
The actions I'm trying to do are:我正在尝试执行的操作是:
Updating a specific item property in array更新数组中的特定项目属性
openingTimes[dayIndex].hours[splitIndex].opens = selectedHour+':'+selectedMinute;
Removing last item of array删除数组的最后一项
openingTimes[i].hours.pop();
Adding an item to array将项目添加到数组
openingTimes[i].hours.push({ opens: 'Abre', closes: 'Cierra' });
And the openingTimes array of objects for reference:以及供参考的对象的 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' } ]},
]);
Updating state on complex data structures gets a little complex because of the immutability issue.由于不变性问题,在复杂数据结构上更新 state 会变得有点复杂。 As you're discovering, you can't just change the state object directly.
正如您所发现的,您不能直接更改 state object。 You actually have to create a copy of the state, then mutate your copy, then call your
setState
with the new copy.您实际上必须创建 state 的副本,然后改变您的副本,然后使用新副本调用您的
setState
。
But React's setState
wants you to provide this new version of your state without changing or mutating the original version.但是 React 的
setState
希望你提供 state 的这个新版本,而不改变或改变原始版本。 This means that every array or object that contains a change needs to be shallow-copied, all the way from the top down to where you made the change.这意味着每个包含更改的数组或 object 都需要进行浅拷贝,从上到下一直到您进行更改的位置。 For a several level deep state like yours, this ends up looking something like this in vanilla JavaScript:
对于像你这样的几级深度 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.
});
Fortunately there is an npm package calledimmutability-helper which does a reasonably good job of making it easier.幸运的是,有一个名为 immutability-helper的 npm package 可以很好地简化它。 (not easy, but easier).
(不容易,但更容易)。 With immutability helper, your mutations would look like this:
使用不变性助手,您的突变将如下所示:
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' } } } }));
Definitely a step in the right direction, but all those nested objects means you have a lot of braces to balance.绝对是朝着正确方向迈出的一步,但是所有这些嵌套对象意味着您有很多大括号需要平衡。 You have a limited selection of $ functions to work with.
您可以使用有限的 $ 函数。 (eg you'll notice we had to use their
$slice
, since they don't have a $pop
.) (例如,您会注意到我们必须使用他们的
$slice
,因为他们没有$pop
。)
As an add-on to immutability-helper, I've often used this small function to simplify things, because all those nested objects in immutability-helper can get a little tedious IMHO.作为 immutability-helper 的附加组件,我经常使用这个小的 function 来简化事情,因为 immutability-helper 中的所有嵌套对象都会变得有点乏味恕我直言。 Using this "update2" helper, you can provide either a string or an array for your "path," and it will build the nested object structure for you.
使用这个“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;
}
This is what it looks like it practice.这就是它的做法。 I'll use an array for the first two paths, and then the last path I'll use the string notation.
我将在前两条路径中使用一个数组,然后在最后一条路径中使用字符串表示法。
// 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.