Search code examples
javascriptreactjsreduxreact-reduxredux-toolkit

Redux Toolkit: How can I store a serialized action creator in state?


Question

I am using Redux Toolkit and I want to store an action creator in state. When I do this, I receive an error regarding non-serializable values in my action as well as my state. Using the code below as an example of my issue, how can I resolve it without just suppressing the warning?

Slice Code

import { ActionCreatorWithoutPayload } from '@reduxjs/toolkit';

export type ModalType =
  {
    type: 'MyModal';
    actionText: string;
    onConfirm: ActionCreatorWithoutPayload;
  }

type ui = {
  modal: ModalType | null;
};

const initialState: ui = {
  modal: null
};

export const slice = createSlice({
  name: 'ui',
  initialState: initialState,
  reducers: {
    showDialog: (state, action: PayloadAction<ModalType>) => {
      state.modal= action.payload;
    },
    someAction: (state) => {
      // do something
    },
  }
});

Component Code

import { someAction } from 'reducers/uiSlice';

<Button
  onClick={() =>
  dispatch(
    showDialog({
      type: 'MyModal', actionText: `Some text`, onConfirm: someAction}
    )
  )}
/>

Error Message

A non-serializable value was detected in an action, in the state: `payload.onConfirm`. Value: ƒ actionCreator() {
    var args = [];

    for (var _i = 0; _i < arguments.length; _i++) {
      args[_i] = arguments[_i];
    }

    if (prepareAction) {
      var prepared = prepareAction.apply(void… 

Solution

  • To be honest, this is a bad idea, and really not at all how Redux should be used.

    Based on the examples, it looks like you're trying to create a modal and pass in a callback as a prop. There are other ways to do this that will work better with Redux, and in fact a few years ago I wrote a post on Managing Modals and Context Menus with Redux. It shows a few different techniques to do this in a way that is compatible with how Redux should be used.

    Today, the option I'd recommend is using a custom middleware that tracks what modals are open, returns a promise from dispatch(showModal()), and resolves the promise when you do dispatch(closeModal(modalId)).

    The post links to https://github.com/AKolodeev/redux-promising-modals as one option for this. I also have an incomplete but mostly working similar implementation in a gist at https://gist.github.com/markerikson/8cd881db21a7d2a2011de9e317007580 that shows some of the potential approaches.