[英]React updating state/setState in .map causes infinite loop
I realize that setTotal(newState) is causing an infinite loop because when it gets updated, it re-renders > causes the function 'showDiscounted' to be called again > state gets updated > and it just goes on forever.我意识到 setTotal(newState) 导致无限循环,因为当它更新时,它会重新呈现 > 导致函数“showDiscounted”再次被调用 > 状态得到更新 > 并且它会永远持续下去。
I know that I have to use useEffect to stop this issue, but I'm just not quite sure how I can implement it.我知道我必须使用 useEffect 来解决这个问题,但我不太确定如何实现它。 I've thought about using useRef because it doesn't cause rerender, but I need it to rerender to show the state 'total' somewhere else.
我考虑过使用 useRef,因为它不会导致重新渲染,但我需要它重新渲染以在其他地方显示状态“总计”。 What is the best way to go about this?
最好的方法是什么?
import { useEffect, useState } from "react";
const QuoteCalcBot = ({ parts, setParts, option, discount }) => {
const [total, setTotal] = useState([0,0,0]);
const [discountedTotal, setdiscountedTotal] = useState([0,0,0]);
//change individual discount for a part
const handleDiscountChange = (part, e) => {
console.log(e.target.value);
console.log(part);
}
//takes in the full price then shows the discounted price of a part. Also adds up the discounted price to discountedTotal for displaying it later
const showDiscounted = (price) => {
const temp = Math.ceil((price * ((100 - discount) / 100)) / 36)
const newState = discountedTotal.map((t, i) => i === option? t + temp : t)
console.log(newState);
setTotal(newState); //this causes infinte loop ERROR
return (
<div className="col-2">
{temp}
</div>
)
}
const addToTotal = (price) => {
//we need to add the full prices to total so it can display
return (
Math.ceil(price / 36)
)
}
//this also works.
//const showParts = (activeIndex) => { return parts.map(part => part.active[activeIndex] && <div key={part.bodyPart}>{part.bodyPart}</div>) }
const showParts = (activeIndex) => {
return parts.map(
(part) => part.active[activeIndex] && (
<div key={part.bodyPart} className="row">
{/* body part */}
<div className="col-3">{part.bodyPart}</div>
{/* original full price */}
<div className="col-2 text-decoration-line-through">${addToTotal(part.price)}</div>
{/* discounted price */}
<div className="col-2">
${(showDiscounted(part.price))}
</div>
{/* choose discount */}
<div className="col-auto">
<select className="form-select-sm" aria-label="Default select example" /* value={discount} */ onChange={(e) => handleDiscountChange(part, e)}>
<option defaultValue>{discount}</option>
<option value="30">30%</option>
<option value="40">40%</option>
<option value="50">50%</option>
<option value="60">60%</option>
</select>
</div>
<div className="col-1 text-end"><button type="button" className="btn-close" aria-label="Close"></button></div>
</div>)
)
}
return (
<div className="">
<div className="row rows-cols-3">
{/* to keep this part cleaner, having a separate state that filters out is ideal */}
<div className="col-4">
{showParts(0)}
</div>
<div className="col-4">
{showParts(1)}
</div>
<div className="col-4">
{showParts(2)}
</div>
{/* TOTAL */}
<div className="col-4">discounted total for 36 months: {total[0]}</div>
<div className="col-4">total, per month, cost, savings</div>
<div className="col-4">total, per month, cost, savings</div>
</div>
</div>
);
}
export default QuoteCalcBot;
Can try to update the state from the event handler(source of the event)可以尝试从事件处理程序(事件源)更新状态
//passing extra params to event handler
const handleDiscountChange = (part, e, partPrice) => {
// this may need extra props to update the price for the right part
const newDiscount = e.target.value
// put this in a common function
const calcPartPrice = Math.ceil((partPrice * ((100 - newDiscount) / 100)) / 36)
const newState = discountedTotal.map((t, i) => i === option? t + calcPartPrice : t)
setTotal(newState);
// update discount
console.log(e.target.value);
console.log(part);
}
better to move this outside as a seperate component最好把它作为一个单独的组件移到外面
const ShowParts = ({ parts = [], activeIndex, discount, handleDiscountChange, addToTotal }) => {
return parts.map(
(part) => {
const partPrice = Math.ceil((part.price * ((100 - discount) / 100)) / 36)
return part.active[activeIndex] && (
<div key={part.bodyPart} className="row">
{/* body part */}
<div className="col-3">{part.bodyPart}</div>
{/* original full price */}
<div className="col-2 text-decoration-line-through">${addToTotal(part.price)}</div>
{/* discounted price */}
<div className="col-2">
<div className="col-2">
{partPrice}
</div>
</div>
{/* choose discount */}
<div className="col-auto">
<select className="form-select-sm" aria-label="Default select example" /* value={discount} */
// pass partPrice and other props needed to handler to help in total calculations
onChange={(e) => handleDiscountChange(part, e, part.price)}
>
<option defaultValue>{discount}</option>
<option value="30">30%</option>
<option value="40">40%</option>
<option value="50">50%</option>
<option value="60">60%</option>
</select>
</div>
<div className="col-1 text-end"><button type="button" className="btn-close" aria-label="Close"></button></div>
</div>)
})
}
usage inside main app主应用程序中的用法
<div className="col-4">
<ShowParts
parts={parts}
activeIndex={0}
discount={discount}
handleDiscountChange={handleDiscountChange}
addToTotal={addToTotal}
/>
</div>
The code can be optimized further, but I hope this helps you in some way代码可以进一步优化,但我希望这能以某种方式帮助你
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.