Redux Toolkit and Axios
Asked Answered
F

2

14

I'm using Redux Toolkit to connect to an API with Axios. I'm using the following code:

const products = createSlice({
    name: "products",
    initialState: {
        products[]
    },
    reducers: {
        reducer2: state => {
            axios
                .get('myurl')
                .then(response => {
                    //console.log(response.data.products);
                    state.products.concat(response.data.products);
            })

        }
    }
});

axios is connecting to the API because the commented line to print to the console is showing me the data. However, the state.products.concat(response.data.products); is throwing the following error:

TypeError: Cannot perform 'get' on a proxy that has been revoked

Is there any way to fix this problem?

Forenamed answered 14/2, 2020 at 5:31 Comment(0)
J
10

It is happening because your reducer function is not a pure function, it should not be having any asynchronous calls.

something like this should work. it will fix the error you are getting

const products = createSlice({
    name: "products",
    initialState: {
        products: []
    },
    reducers: {
        reducer2: (state, { payload }) => {
                return { products: [...state.products, ...payload]}
         })

      }
    }
});

and api should be called outside

const fetchProducts = () => {

   axios.get('myurl')
     .then(response => {
        //console.log(response.data.products);
        store.dispatch(products.actions.reducer2(response.data.products))
   })
};

PS: haven't tried running above code, you may have to make modifications as per your need.

Jasminejason answered 14/2, 2020 at 6:50 Comment(1)
FWIK, return { products: [...state.products, ...payload]} can be shortened as state.products.push(payload) or even (not sure) state.products = payload. This is part of the magic of Redux Toolkit ;)Compete
Y
32

I would prefer to use createAsyncThunk for API Data with extraReducers

Let assume this page name is productSlice.js

import { createSlice,createSelector,PayloadAction,createAsyncThunk,} from "@reduxjs/toolkit";

export const fetchProducts = createAsyncThunk(
  "products/fetchProducts", async (_, thunkAPI) => {
     try {
        //const response = await fetch(`url`); //where you want to fetch data
        //Your Axios code part.
        const response = await axios.get(`url`);//where you want to fetch data
        return await response.json();
      } catch (error) {
         return thunkAPI.rejectWithValue({ error: error.message });
      }
});

const productsSlice = createSlice({
   name: "products",
   initialState: {
      products: [],
      loading: "idle",
      error: "",
   },
   reducers: {},
   extraReducers: (builder) => {
      builder.addCase(fetchProducts.pending, (state) => {
        state. products = [];
          state.loading = "loading";
      });
      builder.addCase(
         fetchProducts.fulfilled, (state, { payload }) => {
            state. products = payload;
            state.loading = "loaded";
      });
      builder.addCase(
        fetchProducts.rejected,(state, action) => {
            state.loading = "error";
            state.error = action.error.message;
      });
   }
});


export const selectProducts = createSelector(
  (state) => ({
     products: state.products,
     loading: state.products.loading,
  }), (state) =>  state
);
export default productsSlice;

In your store.js add productsSlice: productsSlice.reducer in out store reducer.

Then for using in component add those code ... I'm also prefer to use hook

import { useSelector, useDispatch } from "react-redux";

import {
  fetchProducts,
  selectProducts,
} from "path/productSlice.js";

Then Last part calling those method inside your competent like this

const dispatch = useDispatch();
const { products } = useSelector(selectProducts);
React.useEffect(() => {
   dispatch(fetchProducts());
}, [dispatch]); 

And Finally, you can access data as products in your component.

Youngs answered 5/11, 2020 at 10:30 Comment(4)
I applaud this answer and strongly recommend anyone using react-redux read this "redux essentials" documentation in its entirety, and follow these patterns pretty religiously, they are awesome! redux.js.org/tutorials/essentials/part-5-async-logicIncorrigible
This answer is very well detailed. Nothing better have i seen. This is very much it especially the createAsyncThunk. ThanksTecumseh
how about post request? how can we pass the data?Nectarine
@AbhilashPoojary You can have look at here gist.github.com/iammdmusa/9da7e88f59f10eb74822ca2f87e745f4Youngs
J
10

It is happening because your reducer function is not a pure function, it should not be having any asynchronous calls.

something like this should work. it will fix the error you are getting

const products = createSlice({
    name: "products",
    initialState: {
        products: []
    },
    reducers: {
        reducer2: (state, { payload }) => {
                return { products: [...state.products, ...payload]}
         })

      }
    }
});

and api should be called outside

const fetchProducts = () => {

   axios.get('myurl')
     .then(response => {
        //console.log(response.data.products);
        store.dispatch(products.actions.reducer2(response.data.products))
   })
};

PS: haven't tried running above code, you may have to make modifications as per your need.

Jasminejason answered 14/2, 2020 at 6:50 Comment(1)
FWIK, return { products: [...state.products, ...payload]} can be shortened as state.products.push(payload) or even (not sure) state.products = payload. This is part of the magic of Redux Toolkit ;)Compete

© 2022 - 2024 — McMap. All rights reserved.