Search code examples
javascriptreactjsreact-reduxredux-toolkit

How to get headers and data from createAsyncThunk axios


There is a createAsyncThunk in which I use axios to make a request. If I do return res, and I pass action.payload.data to my pizzas array, then everything works, but I get an error in the console:

http://joxi.ru/eAO8VK3CvyXqwr

I understand what needs to be done (return res.data) and then there will be no error.

But I need something to be returned return res, because I need to get res.headers["x-total-count"] from the request elsewhere and pass the quantity to itemsCount

export const fetchPizzas = createAsyncThunk("items/fetchPizzasStatus", async (params) => {
  const { category, sortBy, order, search, perPage, currentPage } = params;
  const res = await axios.get(`http://localhost:3001/items?${category}&q=${search}&_sort=${sortBy}&_order=${order}&_page=${currentPage}&_limit=${perPage}`);
  return res;
});

const initialState = {
  pizzas: [],
  itemsCount: 0,
  status: "loading",
};

export const getItemsSlice = createSlice({
  name: "items",
  initialState,
  reducers: {},
  extraReducers: {
    [fetchPizzas.pending]: (state) => {
      state.status = "loading";
      state.pizzas = [];
    },
    [fetchPizzas.fulfilled]: (state, action) => {
      state.status = "success";
      state.pizzas = action.payload.data;
      state.itemsCount = action.payload.headers["x-total-count"];
    },
    [fetchPizzas.rejected]: (state) => {
      state.status = "error";
      state.pizzas = [];
    },
  },
});

Solution

  • Assuming the API is configured to allow access to all the response headers then I'd suggest unpacking the header value you want/need in the action creator prior to returning a resolved value. The error is that something in the headers object is non-serializable, i.e. like a function.

    Example:

    export const fetchPizzas = createAsyncThunk(
      "items/fetchPizzasStatus",
      async ({ category, sortBy, order, search, perPage, currentPage }) => {
        const { data, headers } = await axios.get(`http://localhost:3001/items?${category}&q=${search}&_sort=${sortBy}&_order=${order}&_page=${currentPage}&_limit=${perPage}`);
    
        const itemCount = headers["x-total-count"];
    
        return { data, itemCount };
      }
    );
    
    const initialState = {
      pizzas: [],
      itemsCount: 0,
      status: "loading",
    };
    
    export const getItemsSlice = createSlice({
      name: "items",
      initialState,
      extraReducers: {
        [fetchPizzas.pending]: (state) => {
          state.status = "loading";
          state.pizzas = [];
        },
        [fetchPizzas.fulfilled]: (state, action) => {
          state.status = "success";
          state.pizzas = action.payload.data;
          state.itemsCount = action.payload.itemCount;
        },
        [fetchPizzas.rejected]: (state) => {
          state.status = "error";
          state.pizzas = [];
        },
      },
    });
    

    This said, it seems like the itemCount state is simply the length of the pizzas array and is easily derived from the existing state. It very likely doesn't need to also be stored in state. I'd recommend just completely removing it.

    export const fetchPizzas = createAsyncThunk(
      "items/fetchPizzasStatus",
      async ({ category, sortBy, order, search, perPage, currentPage }) => {
        const { data } = await axios.get(`http://localhost:3001/items?${category}&q=${search}&_sort=${sortBy}&_order=${order}&_page=${currentPage}&_limit=${perPage}`);
    
        return data;
      }
    );
    
    const initialState = {
      pizzas: [],
      status: "loading",
    };
    
    export const getItemsSlice = createSlice({
      name: "items",
      initialState,
      extraReducers: {
        [fetchPizzas.pending]: (state) => {
          state.status = "loading";
          state.pizzas = [];
        },
        [fetchPizzas.fulfilled]: (state, action) => {
          state.status = "success";
          state.pizzas = action.payload;
        },
        [fetchPizzas.rejected]: (state) => {
          state.status = "error";
          state.pizzas = [];
        },
      },
    });
    
    const pizzas = useSelector(state => state.items.pizzas);
    const itemCount = useSelector(state => state.items.pizzas.length);