Home > Software engineering >  Unhandled Runtime Error TypeError: productsInfos.find(...) is undefined
Unhandled Runtime Error TypeError: productsInfos.find(...) is undefined

Time:12-22

import { useContext, useEffect, useState } from "react"
import Layout from "../components/Layout"
import { ProductsContext } from "../components/ProductsContext"

export default function CheckoutPage(){
    
    const {selectedProducts,setSelectedProducts} = useContext(ProductsContext)
    const [productsInfos,setProductsInfos] = useState([]);
    const [address,setAddress] = useState('');
    const [city,setCity] = useState('');
    const [name,setName] = useState('');
    const [email,setEmail] = useState('');

    useEffect(()=>{
        const uniqIds = [...new Set(selectedProducts)];
        fetch('/api/products?ids=' uniqIds.join(','))
        .then(response => response.json())
        .then(json => setProductsInfos(json));
    },[selectedProducts]);

    function moreOfThisProduct(id){
        setSelectedProducts(prev => [...prev,id])
    }
    function lessOfThisProduct(id){
        const pos = selectedProducts.indexOf(id);
        if(pos !== -1){
            const newSelectedProducts = selectedProducts.filter((value,index) => index !== pos);
            setSelectedProducts(prev => {
                return prev.filter((value,index) => index !== pos);
            });
        }
    }
    const deliveryPrice = 7;
    let subtotal = 0;
    if(selectedProducts?.length){
        for(let id of selectedProducts){
            const price = productsInfos.find(p => p._id === id).price;
            subtotal  = price;
        }
    }
    const total = subtotal   deliveryPrice;

    return (
        <Layout>
            {!productsInfos.length && (
                <div>There is no products in the basket...</div>
            )}
            {productsInfos.length && productsInfos.map(productInfo =>(
                <div className="flex mb-3">
                    <div className="bg-white p-3 rounded-xl shrink-0">
                        <img className="w-20 h-20" src={productInfo.picture}></img>
                    </div>
                    <div className="pl-4">
                        <h3 className="font-bold text-lg">{productInfo.name}</h3>
                        <p className="text-sm leading-4">{productInfo.description}</p>
                        <div className="flex">
                            <div className="grow font-bold">€{productInfo.price}</div>
                            <div>
                                <button onClick={() => lessOfThisProduct(productInfo._id)} className="border border-purple-100 text-purple-100 px-2 rounded-lg">-</button>
                                <span className="px-2">
                                    {selectedProducts.filter(id => id === productInfo._id).length}
                                </span>
                                <button onClick={() => moreOfThisProduct(productInfo._id)} className="bg-black px-2 rounded-lg text-white"> </button>
                            </div>
                        </div>
                    </div>
                </div>
            ))}
            <div className="mt-4">
                <input value={address} onChange={e => setAddress(e.target.value)} className="bg-purple-700 w-full rounded-lg px-4 py-2 mb-1 text-white focus:outline-none placeholder:text-white" type="text" placeholder="Street address"/>
                <input value={city} onChange={e => setCity(e.target.value)} className="bg-purple-700 w-full rounded-lg px-4 py-2 mb-1 text-white focus:outline-none placeholder:text-white" type="text" placeholder="City and postal"/>
                <input value={name} onChange={e => setName(e.target.value)} className="bg-purple-700 w-full rounded-lg px-4 py-2 mb-1 text-white focus:outline-none placeholder:text-white" type="email" placeholder="Name"/>
                <input value={email} onChange={e => setEmail(e.target.value)} className="bg-purple-700 w-full rounded-lg px-4 py-2 mb-1 text-white focus:outline-none placeholder:text-white" type="email" placeholder="Email Address"/>
            </div>
            <div className="mt-4">
                <div className="flex my-3">
                    <h3 className="grow font-bold">Subtotal:</h3>
                    <h3 className="font-bold">€{subtotal}</h3>
                </div>
                <div className="flex my-3">
                    <h3 className="grow font-bold">Delivery Costs:</h3>
                    <h3 className="font-bold">€{deliveryPrice}</h3>
                </div>
                <div className="flex my-3 border-t pt-3 border-dashed border-white">
                    <h3 className="grow font-bold">Total:</h3>
                    <h3 className="font-bold">€{total}</h3>
                </div>
            </div>
        </Layout>
    )
}

Here is my code. I got a problem basicaly I think that it is mongoose because it writes me this stuff

(node:28984) [MONGOOSE] DeprecationWarning: Mongoose: the strictQuery option will be switched back to false by default in Mongoose 7. Use mongoose.set('strictQuery', false); if you want to prepare for this change. Or use mongoose.set('strictQuery', true); to suppress this warning.

But also I got this error

Unhandled Runtime Error

TypeError: productsInfos.find(...) is undefined
Source

pages/checkout.js (37:40) @ CheckoutPage

  35 | if(selectedProducts?.length){
  36 |     for(let id of selectedProducts){
> 37 |         const price = productsInfos.find(p => p._id === id).price;
     |                                    ^
  38 |         subtotal  = price;
  39 |     }
  40 | }

can you please help?

CodePudding user response:

.then(json => setProductsInfos(json));

replace with

.then(json => setProductsInfos(json || []));

update

    if(selectedProducts?.length){
        for(let id of selectedProducts){
            const priceObj = productsInfos.find(p => p._id === id) || {};
            subtotal  = priceObj.price || 0;
        }
    }

CodePudding user response:

I think it's because you're trying to access productInfos at render time, but productInfos is set asynchronously. Remember that useState/setState are also async!

Edit:

All you need to do is check if productInfos is defined before your loop.

  • Related