Search code examples
reactjsreduxreact-reduxredux-toolkitredux-thunk

redux-toolkit createAsyncThunk name is undefined when called


I'm new with typescript and I'd like to implement redux-toolkit for my app, I also want test using asynchronous call. it's actually work with my code but the problem is when I see the log, I see action name is written undefined, is this normal or is there anything I need to add? I believe the action name is not supposed to be undefined? or is there any best practice to call asynchronous call for typescript. I have been looking anywhere and found nothing

import React from "react";
import { useAppSelector, useAppThunkDispatch } from "../hooks/useRedux";
import { alertMessage } from "../store/post";

const List: React.FC = () => {
  const { posts } = useAppSelector((state) => state.post);
  const thunkDispatch = useAppThunkDispatch();
  return (
    <div style={{ marginTop: "20px" }}>
      {posts.map((post) => {
        const onAlert = () => {
          thunkDispatch(alertMessage());
        };

        return (
          <div
            key={post.id}
            style={{
              marginBottom: "20px",
              display: "flex",
              justifyContent: "space-between",
              alignItems: "center",
              maxWidth: "300px",
            }}
          >
            <div>
              <div>{post.title}</div>
            </div>
            <div>
              <button onClick={onAlert}>Alert Message</button>
            </div>
          </div>
        );
      })}
    </div>
  );
};

export default List;

// hooks/useRedux.ts
import { createAsyncThunk } from "@reduxjs/toolkit";
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { AppDispatch, AppThunkDispatch } from "../store/index";
import { RootState } from "../store/reducers";

export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppThunkDispatch: () => AppThunkDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

export const createAppAsyncThunk = createAsyncThunk.withTypes<{
  state: RootState;
  dispatch: AppDispatch;
  rejectValue: string;
  extra: { s: string; n: number };
}>();

import {
  AnyAction,
  configureStore,
  Middleware,
  MiddlewareArray,
} from "@reduxjs/toolkit";
import reducer, { RootState } from "./reducers";
import logger from "redux-logger";
import thunk, { ThunkDispatch } from "redux-thunk";

const customMiddleware: Middleware<{}, RootState> =
  (store) => (next) => (action) => {
    const state = store.getState();

    next(action);
  };

const store = configureStore({
  reducer,
  middleware: new MiddlewareArray()
    .prepend(customMiddleware)
    .concat(logger, thunk),
  devTools: true,
});

export type AppDispatch = typeof store.dispatch;
export type AppThunkDispatch = ThunkDispatch<RootState, any, AnyAction>;

export default store;

import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import produce from "immer";
import { createAppAsyncThunk } from "../hooks/useRedux";

interface IPost {
  userId: number;
  id: number;
  title: string;
  body: string;
}

interface IPosts {
  alertCount: number;
  posts: IPost[];
}

const initialState: IPosts = {
  alertCount: 0,
  posts: [
    {
      id: 0,
      userId: 0,
      title: "this is title",
      body: "this is body",
    },
  ],
};

export const alertMessage = createAppAsyncThunk(
  "post/alertMessage",
  async (_, thunk) => {
    return await callAlert();
  }
);

const callAlert = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      return resolve(alert("Hi"));
    }, 2000);
  });
};

const postSlice = createSlice({
  name: "post",
  initialState,
  reducers: {
    addPost: (
      state,
      action: PayloadAction<{ title: string; body: string }>
    ) => {
      return produce(state, (draftState) => {
        draftState.posts.push({
          id: new Date().getTime(),
          userId: new Date().getTime(),
          ...action.payload,
        });
      });
    },
  },
  extraReducers: (builder) => {
    builder.addCase(alertMessage.fulfilled, (state) =>
      produce(state, (draftState) => {
        draftState.alertCount = draftState.alertCount + 1;
      })
    );
  },
});

export const { addPost } = postSlice.actions;

export default postSlice.reducer;

I think the action name shouldn't be "undefined" log example


Solution

  • If you put the logger before the thunk middleware, it will log those thunks - and since thunks are just functions, not actions, you will just log someFunction.type - which is undefined. The logger belongs after the thunk middleware - and you should probably also be using getDefaultMiddleware.