Search code examples
reactjsreact-reduxredux-toolkit

Redux Toolkit configuration, is it correct for full stack app?


I am new to the redux toolkit, but after long research, I managed to write this configuration for the redux toolkit to fetch data from my backend express API. Can anyone please guide me if I am going right?

Should I use createAsyncThunk or this configuration is also correct?

store.js

import { configureStore } from "@reduxjs/toolkit";
import productReducer from "./features/productSlice";

export const store = configureStore({
  reducer: {
    products: productReducer,
  },
});

productSlice.js

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

const initialState = {
  products: [],
  isLoading: false,
  error: null,
};

export const productsSlice = createSlice({
  name: "products",
  initialState,
  reducers: {
    startLoading: (state) => {
      state.isLoading = true;
    },
    hasError: (state, action) => {
      state.error = action.payload;
      state.isLoading = false;
    },
    allProductsSuccess: (state, action) => {
      state.products = action.payload;
      state.isLoading = false;
    },
  },
});

export default productsSlice.reducer;

productActions.js

import axios from "axios";
import { productsSlice } from "./productSlice";

const { allProductsSuccess, startLoading, hasError } = productsSlice.actions;

export const fetchProducts = () => async (dispatch) => {
  dispatch(startLoading());
  try {
    const { data } = await axios.get("/api/v1/products");

    dispatch(allProductsSuccess(data.products));
  } catch (e) {
    dispatch(hasError(e?.response?.data?.message));
  }
};

Home.js (Component)

const { products, isLoading } = useSelector((state) => state.products);
console.log(products, isLoading)

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

Thanks!


Solution

  • Is it correct? Sure, it looks like it would compile and run and do what you want.

    Is it the recommended implementation? Not exactly. Using the createAsyncThunk utility you can cut down on a little of the boilerplate/repetitive code. You can effectively "merge" the productSlice.js and productActions.js files since all the related actions can be exported from the productSlice.js file. createAsyncThunk generates the "pending"/"success"/"error" actions for you.

    Example:

    import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
    import axios from "axios";
    
    export const fetchProducts = createAsyncThunk(
      "products/fetchProducts",
      async (_, thunkAPI) => {
        try {
          const { data } = await axios.get("/api/v1/products");
    
          return data;
        } catch (e) {
          return thunkAPI.rejectWithValue(e?.response?.data?.message);
        }
      }
    );
    
    const initialState = {
      products: [],
      isLoading: false,
      error: null,
    };
    
    const productsSlice = createSlice({
      name: "products",
      initialState,
      extraReducers: builder => {
        builder
          .addCase(fetchProducts.pending, (state) => {
            state.isLoading = true;
          })
          .addCase(fetchProducts.fulfilled, (state, action) => {
            state.products = action.payload.products;
            state.isLoading = false;
          })
          .addCase(fetchProducts.rejected, (state, action) => {
            state.error = action.payload;
            state.isLoading = false;
          });
      },
    });
    
    export default productsSlice.reducer;