Search code examples
javascriptreactjsobjectreact-hooksuse-effect

useEffect Object dependency infinite loop - ReactJS


I have a State in Context with Object. I am running a async function in the useEffect and passing my object state as dependency.

I passed the state object as dependency because i want to re-render the component when values of settings object changes.

But it causes an infinite loop.

Update:
Let me clear my question more.
In my useEffect, I am calling my async function addTextItem() which calls the function finalPrice and it updates the state, but this way it causes infinite loop.
On other hand, If i call finalPrice in the useEffect directly instead of calling addTextItem then there is no infinite loop but it is also updating the state, right? then how this could be. & I need a solution as i have tried everything and now totally stuck.

Here is the code:

export default function CustomizerState(props) {
    const initialText = "Hello"
    const initialSize = {
        x: REACT_APP_SIZE_X,
        y: REACT_APP_SIZE_Y
    }
    const initialTextItem = {
        id: generateID(),
        text: "Hello",
        font: fonts[0].value,
        size: initialSize,
        color: colors[0],
        backplate: "cut-sharp",
        uvPrint: false,
        quantity: 1,
        neonPrice: 0,
        backplatePrice: 0,
        neonPower: 0,
        totalPrice: 0
    }
    const [settings, setSettings] = useState({
        textItems: [],
        libraryItems: [],
        ownDesigns: [],
        accessories: [
            // My Objects here
        ],
        finalPrice: 0
    })

    const addTextItem = async () => {
        const pathLength = await textToPath(initialText, initialSize.x)
        const { backplatePrice, neonPrice, neonPower, totalPrice } = calculateSvgPrice(pathLength)
        const id = generateID()

        const newItems = [
            ...settings.textItems,
            {...initialTextItem, id, backplatePrice, neonPrice, neonPower, totalPrice}
        ]
        
        finalPrice("textItems", newItems)
    }

    const finalPrice = (itemType = null, items = null) => {
        const textItemsPrice = getTotalPrice()
        const libraryItemsPrice = getTotalPrice("libraryItems")
        const accessoriesPrice = getTotalPrice("accessories", "unitPrice")

        const finalPrice = textItemsPrice + libraryItemsPrice + parseInt(accessoriesPrice)

        if (itemType === null) {
            setSettings((prevState) => (
                {...prevState, finalPrice}
            ))
            return false
        }

        setSettings((prevState) => (
            {...prevState, [itemType]: items, finalPrice}
        ))
    }

    useEffect(() => {
        // It will add first initial form item
        addTextItem()
    }, [JSON.stringify(settings)])

    return (
        <CustomizerContext.Provider value={{settings, addTextItem}}>
            {props.children}
        </CustomizerContext.Provider>
    )
}

enter image description here

I have google and tried the solutions, but nothing worked for me. Can someone help me to fix this issue? I am stuck....


Solution

  • I forgot that when re-rendering happens, it also runs useEffect obviously and it causes setState again and again.
    I managed to fix this by creating a state named initialItemCount with 0 initial value. I am checking if that state value is 0 then run addTextItem which will add my initial form item and updates the final price, but if the value 1 then only run the finalPrice function.

    const [initialItemCount, setInitialItemCount] = useState(0)
    useEffect(() => {
        if (initialItemCount === 0) {
            addTextItem()
            setInitialItemCount(1)
        }
        
        if (initialItemCount === 1) {
            finalPrice()
        }
    }, [JSON.stringify(settings)])
    

    Finally, inner peace.