Search code examples
node.jsreactjsredux

Undefiend data when get from backend api in react


I countered an undefined data from when i get all product form backend I using React for frontend and redux toolkit for handle global state and NodeJS + express JS for backend and database monogodb on atlas cloud

backend:

router.get(
    "/get-all-products",
    catchAsyncErrors(async (req, res, next) => {
        try {
            const products = await Product.find();

            res.status(201).json({
                success: true,
                products,
            });
        } catch (error) {
            return next(new ErrorHandler(error, 400));
        }
    })
);

reducer:

import { createReducer } from "@reduxjs/toolkit";


const initialState = {
    isLoading: true,
};


export const productReducer = createReducer(initialState, {
    getAllProductsRequest: (state) => {
        state.isLoading = true;
    },
    getAllProductsSuccess: (state, action) => {
        state.isLoading = false;
        state.allProducts = action.payload;
    },
    getAllProductsFailed: (state, action) => {
        state.isLoading = false;
        state.error = action.payload;
    },
});

action:

export const getAllProducts = () => async (dispatch) => {
    try {
        dispatch({
            type: "getAllProductsRequest",
        });

        const { data } = await axios.get(`${server}/product/get-all-products`);
        dispatch({
            type: "getAllProductsSuccess",
            payload: data.products,
        });
    } catch (error) {
        dispatch({
            type: "getAllProductsFailed",
            payload: error.response.data.message,
        });
    }
};

Store:

import { configureStore } from '@reduxjs/toolkit';
import { userReducer } from './reducers/user';
import { sellerReducer } from './reducers/seller';
import { productReducer } from './reducers/product';
import { eventReducer } from './reducers/event';

const Store = configureStore({
    reducer: {
        user: userReducer,
        seller: sellerReducer,
        products: productReducer,
        events: eventReducer,
    },
});

jsx page :

 import { useState, useEffect } from "react";
import styles from "../styles/styles";
import { useSearchParams } from "react-router-dom";

import ProductCard from "../components/Route/productCard/ProductCard";
import Header from "../components/layout/Header";
import Loader from "../components/layout/Loader";
import Footer from "../components/layout/Footer";
import { useSelector } from "react-redux";

const ProductsPage = () => {
  const [searchParams] = useSearchParams();
  const categoryData = searchParams.get("category");
  const { allProducts, isLoading } = useSelector((state) => state.products);
  const [data, setData] = useState([]);

  


  useEffect(() => {
    if (categoryData === null) {
      const d = allProducts;
      setData(d);
      console.log(d);
    } else {
      const d =
        allProducts && allProducts.filter((i) => i.category === categoryData);
      setData(d);
    }
    //    window.scrollTo(0,0);
  }, [allProducts]);

  return (
    <>
      {isLoading ? (
        <Loader />
      ) : (
        <div>
          <Header activeHeading={3} />
          <br />
          <br />
          <div className={`${styles.section}`}>
            <div className="grid grid-cols-1 gap-[20px] md:grid-cols-2 md:gap[25px] lg:grid-cols-4 lg:gap-[25px] xl:grid-cols-5 xl:gap-[30px] mb-12">
              {data &&
                data.map((i, index) => <ProductCard data={i} key={index} />)}
            </div>
            {data && data.length === 0 ? (
              <h1 className="text-center w-full  pb-[100px] text-[20px]">
                No Products Found !
              </h1>
            ) : null}
          </div>
          <Footer />
        </div>
      )}
    </>
  );
};

export default ProductsPage;

Solution ------------

const dispatch = useDispatch();

  useEffect(() => {
    dispatch(getAllProducts());
  }, [dispatch]);

  return (
    <>
      {isLoading ? (
        <Loader />
      ) : (
        <div>
          <Header activeHeading={3} />
          <br />
          <br />
          <div className={`${styles.section}`}>
            <div className="grid grid-cols-1 gap-[20px] md:grid-cols-2 md:gap[25px] lg:grid-cols-4 lg:gap-[25px] xl:grid-cols-5 xl:gap-[30px] mb-12">
              {allProducts &&
                allProducts.map((i, index) => (
                  <ProductCard data={i} key={index} />
                ))}
            </div>
            {allProducts && allProducts.length === 0 ? (
              <h1 className="text-center w-full  pb-[100px] text-[20px]">
                No Products Found !
              </h1>
            ) : null}
          </div>
          <Footer />
        </div>
      )}
    </>
  );
};

i need to get raid of this error and know why it's happen


Solution

    1. 201 Created is the wrong response status for your GET request. 200 OK would be much more appropriate and is already the default for res.json().

      res.json({ products });
      
    2. success: true is totally unnecessary. If anything fails, your server will respond with an error response status (4xx / 5xx)

    3. What you're trying to do with the category filter is much more easily achieved using a memo hook

    4. You aren't checking for errors

    const [searchParams] = useSearchParams();
    const categoryData = searchParams.get("category");
    const {
      allProducts,
      isLoading,
      error 
    } = useSelector((state) => state.products);
    
    const data = useMemo(
      () =>
        categoryData
          ? allProducts.filter(({ category }) => category === categoryData)
          : allProducts,
      [allProducts, categoryData]
    );
    
    if (error) {
      return <p className="warn">{error}</p>;
    }
    

    Note: there are no useState or useEffect hooks and most importantly no console.log()! If you want to debug, use debugging tools.

    Also ensure you define the initial state for allProducts as an array so you avoid any errors around .map() or .filter().

    const initialState = {
      isLoading: true,
      allProducts: [], // 👈 initial empty array
    };