Search code examples
javascriptreactjsreact-reduxredux-toolkit

Why am I getting 'A non-serializable value is detected in the state' warning?


I was making a movie review app where I used Redux for storing and fetching the data, even though I left my state empty this error was still showing. Below is the code of the redux movie store.

import { createSlice } from '@reduxjs/toolkit';
import axios from 'axios';

const initialState = {
    
}

const MovieSlice = createSlice({
  name: 'movie',
  initialState,
  reducers: {
    fetchmovie: async (state, action) => {
      console.log(action.payload);
    },
    addmovie: (state) => {

    }
  }
})

export const { fetchmovie }  = MovieSlice.actions;
export default MovieSlice.reducer;

Solution

  • Redux reducer functions are pure, synchronous functions. The fetchmovie reducer is declared async which means it implicitly returns a Promise. Redux-Toolkit slice reducers can either mutate the state or return a new state value. The code is returning a Promise and it's stored in the Redux store as the movie state value.

    Remove the async declaration so fetchmovie is synchronous.

    const MovieSlice = createSlice({
      name: 'movie',
      initialState,
      reducers: {
        fetchmovie: (state, action) => {
          console.log(action.payload);
        },
        addmovie: (state) => { },
      },
    });
    

    If fetchmovie really is supposed to be asynchronous then it needs to be declared as a thunk and the reducer cases handled in the slice's extraReducers property.

    Example:

    import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
    import axios from 'axios';
    
    const initialState = {
        
    }
    
    export const fetchmovie = createAsyncThunk(
      "movie/fetchmovie",
      async (_, thunkApi) => {
        try {
          // ... asynchronous fetching logic
          return data;
        } catch(error) {
          return thunkApi.rejectWithValue(error);
        }
      }
    );
    
    const MovieSlice = createSlice({
      name: 'movie',
      initialState,
      reducers: {
        addmovie: (state) => { },
      },
      extraReducers: builder => {
        builder
          .addCase(fetchmovie.pending, (state, action) => {
            state.loading = true;
          })
          .addCase(fetchmovie.fulfilled, (state, action) => {
            console.log(action.payload);
            state.loading = false;
            ...
          })
          .addCase(fetchmovie.rejected, (state, action) => {
            console.log(action.payload);
            state.loading = false;
            ...
          });
      },
    })
    
    export const { addmovie }  = MovieSlice.actions;
    export default MovieSlice.reducer;