我正在尝试解决 React 中的一个很酷的问题 来自此视频
简而言之,我们需要在商店中有 5 条收银台,以及输入和一个按钮。输入只是一个数字,当我们单击“添加”时,我们基本上将一个拥有 x 数量杂货(x = 输入字段中的数字)的人添加到结账行,其需要处理的杂货总量最少,基本上是一个队列,但我们需要找到杂货总量最少的结账队伍。
每一秒,每个结帐队列中的第一个人都会减少 1,直到达到零,然后我们处理队列中的下一个人。
我尝试在react中解决它并将其分成3个部分:
我不会在这里详细介绍,但主要逻辑发生在
Shop
组件中,这里是完整的,但我将在这里复制一些片段以供上下文使用:
各州:
const [numberOfCheckoutLines, setNumberOfCheckoutLines] = useState<number>(5);
const [checkoutlineToShoppers, setCheckoutlineToShoppers] = useState<CheckoutlineData>(initializeDictionary(numberOfCheckoutLines));
初始化字典的函数,该字典将结帐行的索引映射到空数字数组
function findShopWithMinimumShoppers(checkoutlines: CheckoutlineData) {
let minShopId: number = 0;
let minSum = Infinity;
console.log(checkoutlines)
for (const shopId in checkoutlines) {
const shoppersSum = checkoutlines[shopId].shoppers.reduce((acc, curr) => acc + curr, 0);
if (shoppersSum < minSum) {
minSum = shoppersSum;
minShopId = Number(shopId);
}
}
return minShopId;
}
找到杂货总量最少的结账线的逻辑。
有问题的
useEffect
:
useEffect(() => {
const intervalId = setInterval(() => {
setCheckoutlineToShoppers(prevCheckoutlines => {
const updatedCheckoutlines = { ...prevCheckoutlines };
for (const shopId in updatedCheckoutlines) {
if (updatedCheckoutlines[shopId].shoppers.length > 0) {
// Decrement the first shopper by 1
const firstShopper = updatedCheckoutlines[shopId].shoppers[0];
// Check if the first shopper should be removed
if (firstShopper <= 0) {
// Remove the first shopper
updatedCheckoutlines[shopId].shoppers.shift();
} else {
// Update the first shopper's value
updatedCheckoutlines[shopId].shoppers[0] = firstShopper - 1;
}
}
}
return updatedCheckoutlines;
});
}, 1000); // Every second
return () => clearInterval(intervalId); // Clean up the interval when component unmounts
}, []);
每秒我都会迭代每个结帐行,找到其第一个元素并将其设置为值 -1,但不幸的是,问题是它将每个结帐行中的每个第一个人减少 2 而不是 1...我输入全部 5 个,1 秒后下降到 3,2 秒后全部下降到 1..
这里有什么问题吗?为什么会发生两次?我什至不知道从哪里开始解决这个问题,因为我所做的就是当组件安装时,它只是使用 useEffect 钩子启动一秒的
setInterval
,然后清除它。
提前致谢,如果您确实需要所有源代码,我会尝试找到一种方法来发送所有文件,包括
.css
。
您的间隔回调逻辑中出现了意外的状态突变。
useEffect(() => {
const intervalId = setInterval(() => {
setCheckoutlineToShoppers(prevCheckoutlines => {
const updatedCheckoutlines = { ...prevCheckoutlines };
for (const shopId in updatedCheckoutlines) {
if (updatedCheckoutlines[shopId].shoppers.length > 0) {
// Decrement the first shopper by 1
const firstShopper = updatedCheckoutlines[shopId].shoppers[0];
// Check if the first shopper should be removed
if (firstShopper <= 0) {
// Remove the first shopper
updatedCheckoutlines[shopId].shoppers.shift(); // <-- mutation of updatedCheckoutlines[shopId].shoppers array
} else {
// Update the first shopper's value
updatedCheckoutlines[shopId].shoppers[0] = firstShopper - 1; // <-- mutation of updatedCheckoutlines[shopId] object
}
}
}
return updatedCheckoutlines;
});
}, 1000);
return () => clearInterval(intervalId);
}, []);
prevCheckoutlines
到updatedCheckoutlines
的浅拷贝只是通过引用将属性复制到新的外部对象引用中,但所有属性仍然指向当前状态。
所有 React 状态和嵌套状态,必然需要浅复制 然后 正在更新的部分应该被覆盖。
示例:
useEffect(() => {
const intervalId = setInterval(() => {
setCheckoutlineToShoppers(prevCheckoutlines => {
const updatedCheckoutlines = { ...prevCheckoutlines };
for (const shopId in updatedCheckoutlines) {
if (updatedCheckoutlines[shopId].shoppers.length > 0) {
const firstShopper = updatedCheckoutlines[shopId].shoppers[0];
// Check if the first shopper should be removed
if (firstShopper <= 0) {
// Remove the first shopper
updatedCheckoutlines[shopId] = {
// Shallow copy shop into new object reference
...updatedCheckoutlines[shopId],
// Use slice instead of shift to create new array reference
// and keep all but last element
shoppers: updatedCheckoutlines[shopId].shoppers.slice(0, -1),
};
} else {
// Update the first shopper's value
updatedCheckoutlines[shopId] = {
// Shallow copy shop into new object reference
...updatedCheckoutlines[shopId],
// Use map to create new array reference
shoppers: updatedCheckoutlines[shopId].shoppers.map((shopper, index) => {
return index === 0 ? shopper - 1 : shopper
}),
};
}
}
}
return updatedCheckoutlines;
});
}, 1000);
return () => clearInterval(intervalId);
}, []);