addy
addy

Reputation: 83

TypeError: Cannot read property 'length' of undefined in react js

My code is supposed to generate a popup of a product then add the product to the wishlist page. It gives an error that length is not defined, I think it because I misused hooks, I tried adding const [listVariations, setListVariations] = useState(0) in place of const [listVariations, setListVariations] = useState([]) but it still gives out same error can someone help me sort it out thanks in advance!

import React, { Fragment, useContext, useState, useEffect} from 'react'
import './Sass/Index.scss'
import { WishListContext } from '../../contexts/WishListContext'
import { ProductContext } from '../../contexts/ProductContext'
import Popup from "reactjs-popup";

const Product = ({product, id, productIndex}) => {
    const{name, price, image, variation, option, unit, total} = product
    const {wishListState, wishListDispatch} = useContext(WishListContext)
    const {productState} = useContext(ProductContext)
    const [popupVariation, setPopupVariation] = useState(false)
    const [popupOption, setPopupOption] = useState(false)
    const [listVariations, setListVariations] = useState([])
    const [listOptions, setListOptions] = useState([])
    const [selectedOption, setSelectedOption] = useState({})
    const [updateToggle, setUpdateToggle] = useState(false)
   

    useEffect(()=> {
        generatePopupVariation()
        generatePopupOptions()
    }, [productState])

    const updateLocalStorage = (products) => {
        if (products.length > 0) {
            wishListDispatch({
                type:"REPLACE_WISH",
                payload: products.map(item => item)
            })
        } 
    }

    const generatePopupOptions = () => {
        const products = productState.products
        if (products.length > 0) {
            const finedProduct = products.find(item => item.id === id)
            const priceOptions = finedProduct?.data.priceOptions
            setListOptions(priceOptions && priceOptions[0].options) 
        }
    }

    const generatePopupOptionContent= () => {
        if ( listOptions.length > 0 ) {
           const filtered = listOptions.filter(item => item.is_available !== 'No')
           return filtered.map((item, index) => <li key={index}><span>{item.option}</span><span>Price: {item.price}</span><div className="btn btn-primary" onClick={() => changeOption(index, item.option, item.price)} >Select</div></li>)
        }
    }

    const changeOption = (index, option, price) => {
        const action = {
            id,
            option,
            price
        }
        if (option) {
            wishListDispatch({
                type:'UPDATE_OPTION_WISH',
                action
            })
            updateLocalStorage(wishListState.products)
            modalOption('close')
        }  
    }

  
    const generatePopupVariation = () => {
        const products = productState.products
        if (products.length > 0) {
            const finedProduct = products.find(item => item.id === id)
            const priceOptions = finedProduct?.data.priceOptions
            setListVariations(priceOptions)
        }
    }

    const generatePopupVariationContent = () => {
     
     if ( listVariations.length > 0 ) {
      console.log(listVariations.length)
      return listVariations.map((item, index) => <li className="btn btn-primary" onClick={() => changeVariation(index, item.variation, item.options)} key={index}>{item.variation}</li>)
   }
    }

    const changeVariation = (index, variation, options) => {
        const action = {
            id,
            variation
        }
        if (variation) {
            wishListDispatch({
                type:'UPDATE_VARIATION_WISH',
                action
            })
            updateLocalStorage(wishListState.products)
            modalVariation('close')
        }  
    }
    
    const handleRemove = () => {
        if (wishListState) {
            if( wishListState.products.length > 0) {
                wishListDispatch({
                    type: 'REMOVE_WISH',
                    payload: productIndex
                })
            }
        }
    }
    const handleUnit = (control) => {
        if (wishListState) {
            if( wishListState.products.length > 0) {
                wishListDispatch({
                    type: 'ADD_UNIT_WISH',
                    payload: {index: productIndex, control:control}
                })
            }
        }
    }
    const modalVariation = action => {
        if (action === 'open') {
            setPopupVariation(true)
        } else if (action === 'close') {
            setPopupVariation(false)
        }
    }
    const modalOption = action => {
        if (action === 'open') {
            setPopupOption(true)
        } else if (action === 'close') {
            setPopupOption(false)
        }
    }

    return (
        <Fragment>
            <Popup
                open={popupVariation}
                closeOnDocumentClick
                onClose={()=> modalVariation('close')}
            >
                <div className="popup-content"> 
                    <ul>
                        {generatePopupVariationContent()}
                    </ul>
                    <span onClick={() => modalVariation('close')}>X</span>
                </div>
            </Popup>
            <Popup
                open={popupOption}
                closeOnDocumentClick
                onClose={()=> modalOption('close')}
            >
                <div className="popup-content"> 
                    <ul>
                        {generatePopupOptionContent()}
                    </ul>
                    <span onClick={() => modalOption('close')}>X</span>
                </div>
            </Popup>
                <div className="card">
                    <div className="card-wrapper">
                        <div className="left">
                            <img className="card-img" src={ image } alt={ name } />
                        </div>
                        <div className="right">
                            <div className="card-remove" onClick={handleRemove}>
                                <i className="times" name="close"></i>
                            </div>
                            <h4 className="card-title">{ name }</h4>
                            { variation && <p className="card-variation"><span>Variation:</span><span>{variation} <span onClick={()=> modalVariation('open')} className="change-variation"><ion-icon name="create"></ion-icon></span></span> </p>}
                            { option && <p className="card-option"><span>Option:</span><span> { option }  <span onClick={()=> modalOption('open')} className="change-option"><ion-icon name="create"></ion-icon></span></span></p>}
                            <div className="card-unit"><span>Qty:</span>
                                <div className="card-units-wrapper">
                                    <i className="plus" name="add" onClick={() => handleUnit('+')}></i>
                                    <div className="card-units">{unit}</div>
                                    <i className="minus" name="remove" onClick={() => handleUnit('-')}></i>
                                </div>
                            </div>
                            <p className="card-price"><span>Price:</span><span> {price} </span></p>
                            <p className="card-total"><span>Total:</span> <span>{total} </span></p>
                        </div>
                    </div>
                </div>
        </Fragment>
    )
}

export default Product

ProductContext.js

import React, { createContext, useReducer } from 'react'
import ProductReducer from '../reducers/ProductReducer'

export const ProductContext = createContext()

const initState = {
    products: []
}

const ProductProvider = props => {
    const [ productState, productDispatch ] = useReducer(ProductReducer, initState)
    const value = {productState, productDispatch}
    return (
        <ProductContext.Provider value={value}>
            {props.children}
        </ProductContext.Provider>
    )
}

export default ProductProvider

ProductReducer.js

const ProductReducer = (state, action) => {
    switch (action.type) {
        case "LOAD_PRODUCTS":
            return {...state, products: [...action.payload]}
        case "ADD_PRODUCT":
            return {...state, products: [...state.products, action.payload]}
        default:
            return state
    }
}
export default ProductReducer;

WishlistContext.js

import React, { createContext, useReducer } from 'react'
import WishListReducer from '../reducers/WishListReducer'
export const WishListContext = createContext()

const initState = {
    products: [],
    select_variation:null
}
const WishListProvider = props => {
    const [ wishListState, wishListDispatch ] = useReducer(WishListReducer, initState)
    const value = {wishListState, wishListDispatch }
    return (
        <WishListContext.Provider value={value}>
            {props.children}
        </WishListContext.Provider>
    )
}

export default WishListProvider

WishlistReducer.js

const WishListReducer = (state, action) => {
    switch (action.type) {
        case "ADD_WISH":
            return addWish(state, action)
        case "REPLACE_WISH":
            return replaceWish(state, action)
        case "REMOVE_WISH":
            return removeWish(state, action)
        case "ADD_UNIT_WISH":
            return unitWish(state, action)
        case "UPDATE_VARIATION_WISH":
            return updateVariation(state, action)
        case "UPDATE_OPTION_WISH":
            return updateOption(state, action)
        default:
            return state
    }
}

const addWish = (state, action) => {
    localStorage.setItem('wish-list', JSON.stringify({...state, products: [...state.products, action.payload] }))
    return {...state, products: [...state.products, action.payload] }
}

const replaceWish = (state, action) => {
    localStorage.setItem('wish-list', JSON.stringify({...state, products: [...action.payload] }))
    return {...state, products: [...action.payload] }
}

const removeWish = (state, action) => {
    const copyState = {...state }
    copyState.products.splice(action.payload, 1);
    localStorage.setItem('wish-list', JSON.stringify({...state, products: [...copyState.products] }))
    return {...state, products: [...copyState.products] }
}

const unitWish = (state, action) => {
    const copyState = {...state }
    const index = copyState.products[action.payload.index]
    const ctrl = action.payload.control
    const unit = index.unit
    const price = index.price
    if (ctrl == "+") {
        index.unit = Number(unit) + 1
        index.total = Number(price) * (Number(unit) + 1)
    }
    if (ctrl == "-") {
        if (Number(unit) <= 1) {
            index.unit = 1
        } else {
            index.unit = Number(unit) - 1
            index.total = Number(price) * (Number(unit) - 1)
        }
    }
    localStorage.setItem('wish-list', JSON.stringify({...state, products: [...copyState.products] }))
    return {...state, products: [...copyState.products] }
}

const updateVariation = (state, action) => {
    const id = action.action.id
    const variation = action.action.variation
    const copyState = {...state }
    const product = copyState.products.find(item => item.id == id)
    product.variation = variation
    return {...state, products: [...copyState.products] }
}

const updateOption = (state, action) => {
    const id = action.action.id
    const option = action.action.option
    const price = action.action.price
    const copyState = {...state }
    const product = copyState.products.find(item => item.id === id)
    product.option = option
    product.price = price
    product.total = product.unit * price
    return {...state, products: [...copyState.products] }
}


export default WishListReducer;

Upvotes: 2

Views: 785

Answers (1)

cangokceaslan
cangokceaslan

Reputation: 482

Basically, some of the content used with .length is undefined. The best way to learn which one is undefined, is to use typeof. You can use typeof variableName to determine what is the type of the variable. If some of the types in the code are not List or Array, it will create an error. To debug, you can put alert("Type is not valid") combined with methods to see. (Btw we are not having those data and determine what is errored.)

Upvotes: 1

Related Questions