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>
)
}
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);
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.