简体   繁体   English

React updating state/setState in.map 导致死循环

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM