Search code examples
reactjsnext.jsreact-context

NextJS: Context API for storing and passing values


I am building this project on NextJS where I want to build a shopping cart. For that, I decided to use useContext hook. So basically what I did is pretty basic. Create a ShoppingCartContext, add a addToCart function which will push the data to store. But when I press the add to cart button it shows me that,

store.push is not a function.

edit: When I press the button first time, nothing happens. When I press it the 2nd time it gives the mentioned error. I think I did some basic stuff wrong in the code which is why I am getting this error. My context code

import {
  createContext,
  useCallback,
  useContext,
  useState,
  useEffect,
} from "react";

const ShoppingCartContext = createContext();

export function ShoppingCartContextProvider({ children }) {
  const [store, setStore] = useState([]);

  return (
    <ShoppingCartContext.Provider
      value={{
        store,
        setStore,
      }}
    >
      {children}
    </ShoppingCartContext.Provider>
  );
}

export function useShoppingCartContext() {
  const { store, setStore } = useContext(ShoppingCartContext);

  //function to push data to array
  const addToCart = (product) => {
    setStore(store.push(product));
  };

  return {
    store,
    setStore,
    addToCart,
  };
}

Where I called the button component to add data

import { useRouter } from "next/router";
import { useState } from "react";
import { useShoppingCartContext } from "../../context/CartContext";

const SingleProduct = ({ product, categories }) => {
  const { store, setStore, addToCart } = useShoppingCartContext();
  const router = useRouter();
  const breadcrumb = router.components["/shop/[[...category]]"].resolvedAs;

  const [count, setCount] = useState(0);
  const increment = () => {
    setCount(count + 1);
  };
  const decrement = () => {
    setCount(count - 1);
  };

  return (
    <>
   
        <Button
              onClick={() => {
                addToCart(product); // product is basically an object which holds all the info of the single product
              }}
            >
              Add to Cart
            </Button>
    </>
   )
} 

my _app.js file

import "../styles/globals.css";
import { ChakraProvider } from "@chakra-ui/react";
import { ShoppingCartContextProvider } from "../context/CartContext";

function MyApp({ Component, pageProps }) {
  return (
    <ChakraProvider>
      <ShoppingCartContextProvider>
        <Component {...pageProps} />
      </ShoppingCartContextProvider>
    </ChakraProvider>
  );
}

export default MyApp;

please take a look at this.


Solution

  • For updating state which is array you need to do

    setStore(store => [...store, product])
    
    //OR 
    
    // make clone of store and then setStore
    temp=[...store]
    temp.push(product)
    setStore(temp)
    

    EXPLANATION OF CURRENT BEHAVIOUR

    1. In your case it's working 1st time as you have initialized store as [] empty array.
    2. And because it's an array it has .push() method.
    3. But [].push(val) will not return an array it will return the length of array.
    4. And therefore after 1st time store get's the value of numeric datatype which does not has .push() method.

    Below you can see the result of .push() your self.

    arr=[1,2,3] // length 3
    console.log(`initial array`)
    console.log(arr)
    console.log(`spread operator + new val`)
    console.log([...arr,5]) // spread operator + new val
    console.log(`.push()`)
    console.log(arr.push(5),"length=",arr.length) // .push() this will return length of the arr