Search code examples
reactjsreact-reduxredux-thunk

Unhandled Rejection (TypeError): state.push is not a function while using redux thunk


I'm getting this error Unhandled Rejection (TypeError): state.push is not a function while using redux thunk but while refrshing the page after error, new word is getting added to the DB.

Below is my code.

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

export const getDictionaryAsync = createAsyncThunk(
  "dictionary/getDictAsync",
  async () => {
    let res = await fetch("https://vocabulary-app-be.herokuapp.com/dictionary");
    if (res.ok) {
      let dictData = await res.json();
      return { dictData };
    }
  }
);

export const addWordtoDictAsync = createAsyncThunk(
  "dictionary/addWordtoDictAsync",
  async (payload) => {
    let res = await fetch(
      "https://vocabulary-app-be.herokuapp.com/dictionary",
      {
        method: "POST",
        body: JSON.stringify({ word: payload.word }),
        headers: {
          "Content-Type": "application/json",
        },
      }
    );
    if (res.ok) {
      let data = await res.json();
      console.log(data);
      return { data };
    }
  }
);

const dictionarySlice = createSlice({
  name: "dictionary",
  initialState: [],
  reducers: {},
  extraReducers: {
    [getDictionaryAsync.fulfilled]: (state, action) => {
      return action.payload.dictData;
    },
    [addWordtoDictAsync.fulfilled]: (state, action) => {
      console.log(action.payload.data + "reducer");
      state.push(action.payload.data);
    },
  },
});

export default dictionarySlice.reducer;

Solution

  • Issue

    The issue is that the first call to get the dictionary mutates the state invariant, from array to object. The JSON response object from "https://vocabulary-app-be.herokuapp.com/dictionary" is an object with message and data keys.

    {
      "message": "Your data is here",
      "data": [ .... your dictionary data array ]
    }
    

    The getDictionaryAsync returns an object with dictData key.

    export const getDictionaryAsync = createAsyncThunk(
      "dictionary/getDictAsync",
      async () => {
        let res = await fetch("https://vocabulary-app-be.herokuapp.com/dictionary");
        if (res.ok) {
          let dictData = await res.json();
          return { dictData }; // <-- returned in action payload
        }
      }
    );
    

    And the reducer case sets state to this payload value.

    [getDictionaryAsync.fulfilled]: (state, action) => {
      return action.payload.dictData;
    },
    

    Now your state is:

    {
      "message": "Your data is here",
      "data": [ .... your dictionary data array ]
    }
    

    And can't be pushed into.

    Solution

    I think you just want the dictData.data array as the payload, or just the data property straight from a returned dictData object.

    export const getDictionaryAsync = createAsyncThunk(
      "dictionary/getDictAsync",
      async () => {
        let res = await fetch("https://vocabulary-app-be.herokuapp.com/dictionary");
        if (res.ok) {
          let dictData = await res.json();
          return dictData.data; // <-- returned data property as payload
        }
      }
    );
    
    ...
    
    [getDictionaryAsync.fulfilled]: (state, action) => {
      return action.payload; // <-- return data payload as state
    },