0

hello everyone I am aking a MERN project and Im running into a peobleme. so Im using redux toolkit slice and when I try to dispatch the fetchProduct like so

import React, { useEffect, useState } from "react";
import "./products.css";
import { useSelector, useDispatch } from "react-redux";
import { DataGrid } from "@mui/x-data-grid";
import { Link, Navigate } from "react-router-dom";
import DeleteForeverIcon from "@mui/icons-material/DeleteForever";
import Loader from "components/Loader";
import { Button } from "@mui/material";
import { useNavigate } from "react-router-dom";
import Message from "components/Message";
import { fetchProducts } from "slices/productSlice";

const Products = () => {
  const dispatch = useDispatch();
  const navigate = useNavigate();

  const { loading, error, products } = useSelector((state) => state.product);

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


  const columns = [
    { field: "_id", hide: true },
    {
      field: "name",
      flex: 1,
      headerName: "Product",
      width: 200,
      renderCell: (params) => {
        return (
          <div className="productListItem">
            <img className="productListImg" src={params.row.image} alt="" />
            {params.row.name}
          </div>
        );
      },
    },
    { field: "countInStock", headerName: "Stock", flex: 1 },
    {
      field: "price",
      headerName: "Price",
      flex: 1,
    },
    {
      field: "brand",
      headerName: "Brand",
      flex: 1,
    },
    {
      field: "action",
      headerName: "Action",
      flex: 1,
      renderCell: (params) => {
        return (
          <>
            <Link to={"/products/" + params.row._id}>
              <button className="productListEdit">Edit</button>
            </Link>
            <DeleteForeverIcon
              className="productListDelete"
              // onClick={() => deleteHandler(params.row._id)}
            />
          </>
        );
      },
    },
  ];

  return (
    <div style={{ height: "90vh" }}>
      {loading ? (
        <Loader />
      ) : error ? (
        <Message variant="error" />
      ) : (
        <DataGrid
          height={100}
          getRowId={(row) => row._id}
          rows={products?.products}
          disableSelectionOnClick
          columns={columns}
          rowsPerPageOptions={[10, 15, 20]}
          pageSize={10}
          checkboxSelection
        />
      )}    
      
    </div>
  );
};

export default Products;

the data never get fetched and I get this error in the browser: "Warning: Failed prop type: The prop rows is marked as required in ForwardRef(DataGrid), but its value is undefined."

but when I remove what's inside the return of the componenet and add just an h1 tag the data get fetched and I can find it in redux store states

this is the slice I am working with :

import axios from 'axios';
import { createSlice } from '@reduxjs/toolkit';

const initialState = {
  products: [],
  loading: false,
  error: null
};
const baseurl = "http://localhost:5001"

const productSlice = createSlice({
  name: 'product',
  initialState,
  reducers: {
    fetchProductsStart(state) {
      state.loading = true;
    },
    fetchProductsSuccess(state, action) {
      state.products = action.payload;
      state.loading = false;
      state.error = null;
    },
    fetchProductsError(state, action) {
      state.loading = false;
      state.error = action.payload;
    },
    deleteProductStart(state) {
      state.loading = true;
    },
    deleteProductSuccess(state, action) {
      state.products = state.products.filter(product => product._id !== action.payload);
      state.loading = false;
      state.error = null;
    },
    deleteProductError(state, action) {
      state.loading = false;
      state.error = action.payload;
    },
    updateProductStart(state) {
      state.loading = true;
    },
    updateProductSuccess(state, action) {
      const index = state.products.findIndex(product => product._id === action.payload._id);
      state.products[index] = action.payload;
      state.loading = false;
      state.error = null;
    },
    updateProductError(state, action) {
      state.loading = false;
      state.error = action.payload;
    },
    fetchProductStart(state) {
      state.loading = true;
    },
    fetchProductSuccess(state, action) {
      state.products = [action.payload];
      state.loading = false;
      state.error = null;
    },
    fetchProductError(state, action) {
      state.loading = false;
      state.error = action.payload;
    }
  }
});

export const {
  fetchProductsStart,
  fetchProductsSuccess,
  fetchProductsError,
  deleteProductStart,
  deleteProductSuccess,
  deleteProductError,
  updateProductStart,
  updateProductSuccess,
  updateProductError,
  fetchProductStart,
  fetchProductSuccess,
  fetchProductError
} = productSlice.actions;
export default productSlice.reducer


export const fetchProducts = () => async dispatch => {
  try {
    dispatch(fetchProductsStart());
    const response = await axios.get(`${baseurl}/products`);
    dispatch(fetchProductsSuccess(response.data));
  } catch (error) {
    dispatch(fetchProductsError(error.message));
  }
};

export const deleteProduct = id => async dispatch => {
  try {
    dispatch(deleteProductStart());
    await axios.delete(`${baseurl}/products/${id}`);
    dispatch(deleteProductSuccess(id));
  } catch (error) {
    dispatch(deleteProductError(error.message));
  }
};

export const updateProduct = product => async dispatch => {
  try {
    dispatch(updateProductStart());
    const response = await axios.patch(`${baseurl}/products/${product._id}`, product);
    dispatch(updateProductSuccess(response.data));
  } catch (error) {
    dispatch(updateProductError(error.message));
  }
};

so what am I doing wrrong here

the modified Slice

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

const initialState = {
  products: [],
  loading: 'idle',
  error: null
};

const baseurl = 'http://localhost:5001';

export const fetchProducts = createAsyncThunk(
  'products/fetchProducts',
  async () => {
    const response = await axios.get(`${baseurl}/products`);
    return response.data;
  }
);

export const deleteProduct = createAsyncThunk(
  'products/deleteProduct',
  async id => {
    await axios.delete(`${baseurl}/products/${id}`);
    return id;
  }
);

export const updateProduct = createAsyncThunk(
  'products/updateProduct',
  async product => {
    const response = await axios.patch(
      `${baseurl}/products/${product._id}`,
      product
    );
    return response.data;
  }
);

export const fetchProduct = createAsyncThunk(
  'products/fetchProduct',
  async id => {
    const response = await axios.get(`${baseurl}/products/${id}`);
    return response.data;
  }
)


const productSlice = createSlice({
  name: 'products',
  initialState,
  reducers: {},
  extraReducers: {
    [fetchProducts.pending]: state => {
      state.loading = 'pending';
    },
    [fetchProducts.fulfilled]: (state, action) => {
      state.loading = 'idle';
      state.products = action.payload;
    },
    [fetchProducts.rejected]: (state, action) => {
      state.loading = 'idle';
      state.error = action.error.message;
    },
    [deleteProduct.pending]: state => {
      state.loading = 'pending';
    },
    [deleteProduct.fulfilled]: (state, action) => {
      state.loading = 'idle';
      state.products = state.products.filter(product => product._id !== action.payload);
    },
    [deleteProduct.rejected]: (state, action) => {
      state.loading = 'idle';
      state.error = action.error.message;
    },
    [updateProduct.pending]: state => {
      state.loading = 'pending';
    },
    [updateProduct.fulfilled]: (state, action) => {
      state.loading = 'idle';
      const index = state.products.findIndex(product => product._id === action.payload._id);
      state.products[index] = action.payload;
    },
    [updateProduct.rejected]: (state, action) => {
      state.loading = 'idle';
      state.error = action.error.message

Joseph
  • 11
  • 4
  • Do you think the result is empty, you can try ` rows={products?.products ?? []}` – Azzy Jan 06 '23 at 17:48
  • I tried it befor still nothing.the thing is when ever I try to access the product data in any way and keep an eye on redux store the fetchproduct never get dispatched – Joseph Jan 06 '23 at 17:55
  • So initially `state.product.products` is an array, then later it is an object with another nested `products` property, e.g. `state.product.products.products`? It seems you should fix your store to provide a consistent invariant. What is the `fetchProducts` response value? Also, you are already using `redux-toolkit`, why not use `createAsyncThunk` instead of hand writing all the async actions manually? – Drew Reese Jan 07 '23 at 00:55
  • @DrewReese I am knew with redux-toolkit and I have no Idea on how to impliment this reduc "CreateAsyncthunk" could you give me a hint please – Joseph Jan 07 '23 at 13:04
  • @DrewReese Could you pleaze check the modified SLice I just added to the question and tell me what you think ? – Joseph Jan 07 '23 at 13:14
  • Quick glance it all looks ok to me. The main issue was that it seemed like at least one action was changing the `state.product.products` state from an array to an object, so as long as you are maintaining an array state invariant it should work either way. – Drew Reese Jan 08 '23 at 20:16

0 Answers0