Search code examples
javascriptnode.jsmongodbreduxredux-toolkit

Loading Data from MongoDB into Redux Store fetching


After successfully setting up my Node API and MongoDB, my current focus is on integrating Redux into my application. I'll try to share code snippets here.

Server Order controller works as expected :

const getTotal = asyncHandler(async (req, res) => {

  const monthly = await Order.aggregate([
    {
      $group: {
        _id: { month: { $month: "$createdAt" }, year: { $year: "$createdAt" } },
        price: { $sum: "$price" },
      },
    },
  ]);

  res.status(200).json(monthly);
});

I would like to get the data into react redux but I'm facing a problem. I setup my store in (store.js) as follows:

import { configureStore } from "@reduxjs/toolkit";
import { orderReducer } from "./orderSlice";
import { authReducer } from "./authSlice";



const store = configureStore({
    reducer: {
        auth: authReducer,
       order: orderReducer,
       
    }
});

export default store;

and here order slice

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

const orderSlice = createSlice({
   name: "order",
   initialState: {
    orders: [],
    order: null,
    total: [""],
   },
   reducers: {
      addOrder(state, action) {
         state.orders.push(action.payload);
      },  
      setOrders(state, action) {
         state.orders = action.payload;
       },
       setOrder(state,action) {
         state.order = action.payload;
       },
       setTotal(state,action) {
         state.total = action.payload;
       },
   }
});

const orderReducer = orderSlice.reducer;
const orderActions = orderSlice.actions;

export { orderActions, orderReducer }

and the order Api call to fetch total:

  export function fetchTotal() {
    return async (dispatch) => {
      try {
        const { data } = await request.get(`/api/orders/total`);
        dispatch(orderActions.setTotal(data));
      } catch (error) {
        toast.error(error.response.data.message);
      }
    };
  }

so now I am trying to retrieve and Display Data in table but it doesn't work

import React, { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux';
import { fetchTotal } from './redux/orderApiCall';

export default function Total() {
  const dispatch = useDispatch();
  const { ordersTotal } = useSelector(state => state.order);

  useEffect(() => {
    dispatch(fetchTotal());
  }, []);
  return (
    <table className="table">
    <thead>
      <tr>
        <th>Id</th>
        <th>Month</th>
        <th>Price</th>
      </tr>
    </thead>
    <tbody>
      {ordersTotal?.map((item,index) => (
        <tr key={item._id}>
          <td>{index + 1}</td>
                  <td>{item?.price}</td>
        </tr>
      ))}
    </tbody>
  </table>

  )
}

Solution

  • fetchTotal is a function that returns an asynchronous function which accepts dispatch as an arguments, so I'm assuming dispatch(fetchTotal()) is not the correct syntax. Try this:

    fetchTotal()(dispatch);
    

    Alternatively:

    You can convert your fetchTotal function to a Redux Thunk using createAsyncThunk():

    const fetchTotal = createAsyncThunk(
       'total/getTotla', //action type string
       // Callback function
       async (thunkAPI) => {
        const {data} = request.get(`/api/orders/total`)
       )
       return data
    })
    

    And then add reducers to your slice to update your state:

    const orderSlice = createSlice({
    name: "order",
    initialState: {
    orders: [],
    order: null,
    total: [""],
    loading: false // Indicates if thunk data is ready
    },
    reducers: {
       // ...
    }
    extraReducers: {
       [getTotal.pending]: (state) => {
         state.loading = true
       },
       [getTotal.fulfilled]: (state, { payload }) => {
         state.loading = false
         state.total = payload
       },
       [getTotal.rejected]: (state) => {
         state.loading = false
       },
      },
    });
    

    This is the standard async request lifecycles approach recommended by @reduxjs/toolkit developers, it allows the redux store to be in total control of the asynchronous request and whatever result or errors it may produce.