中间件中的无限循环调度动作

Infinite loop dispatching action in middleware

我正在尝试创建一个刷新登录中间件(在即将过期时获取一个新的 jwt 令牌)。但是,当我尝试注销我的用户以防令牌已经过期时,应用程序冻结并且我收到最大调用堆栈错误(无限循环)。我目前正在使用带有 configureStore 和 createSlice 的 Redux Toolkit。在控制台日志中,它会打印很多 DISPATCHING 但如果我在 createSlice 中的操作中控制台记录任何内容,它不会打印任何内容。所以...我认为问题出在调度操作上

// auto-login.middleware.js

const autoLogin =
  ({ getState, dispatch }) =>
  (next) =>
  (action) => {
    const { user } = getState()

    if (user) {
      const expDatetime = moment(user.exp * 1000)
      const tokenExpired = expDatetime < moment()

      if (!tokenExpired && moment() >= expDatetime.subtract(15, 'm')) {
        console.log('NOT EXPIRED')
        const { data } = apolloClient.mutate({ mutation: RENEW_TOKEN })
        dispatch(setUser(data.RenewToken))
      } else {
        // I saw this in a different post and tried, but still no resolutions:
        // return dispatch(setUser(null)).then(() => next(action))
        console.log('DISPATCHING')
        dispatch(setUser(null))
        console.log('DISPATCH COMPLETE')
      }
    }

    return next(action)
  }

export default autoLogin
// user.slice.js

import { createSlice } from '@reduxjs/toolkit'

const userSlice = createSlice({
  name: 'user',
  initialState: null,
  reducers: {
    setUser: (_, action) => {
      console.log('ENTERED?')
      return action.payload
    },
  },
})

const { actions, reducer } = userSlice

export const { setUser } = actions
export default reducer
// store.js

const persistConfig = {
  key: 'root',
  storage,
}

const persistedReducer = persistReducer(
  persistConfig,
  combineReducers({
    user: userReducer,
    categories: categoriesReducer,
    cart: cartReducer,
  })
)

export const store = configureStore({
  reducer: persistedReducer,
  devTools: process.env.NODE_ENV !== 'production',
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
      },
    }).concat(autoLogin, logger),
})

export const persistor = persistStore(store)

来自控制台日志的错误(如果我在 configureStore 中将 immutableCheck 设置为 false,我仍然会收到错误,但我从 moment.js 中得到,这对我来说没有意义):

immutableStateInvariantMiddleware.ts:126 Uncaught (in promise) RangeError: Maximum call stack size exceeded
at detectMutations (immutableStateInvariantMiddleware.ts:126:1)
at detectMutations (immutableStateInvariantMiddleware.ts:161:1)
at detectMutations (immutableStateInvariantMiddleware.ts:161:1)
at detectMutations (immutableStateInvariantMiddleware.ts:161:1)
at detectMutations (immutableStateInvariantMiddleware.ts:161:1)
at detectMutations (immutableStateInvariantMiddleware.ts:161:1)
at Object.detectMutations (immutableStateInvariantMiddleware.ts:86:1)
at immutableStateInvariantMiddleware.ts:246:1
at Object.measureTime (utils.ts:9:1)
at immutableStateInvariantMiddleware.ts:243:1

经过询问和搜索,我了解到 Redux 中间件在任何 Action 发生之前执行,这就是我遇到递归问题的原因 - 每次调用调度时我的中间件都是 re-executing。在检查操作类型是否与我在中间件中进行的调度相同之后,递归停止了,因为我的调度仅在调用不同的操作时才被触发。

// auto-login.middleware.js

const autoLogin =
  ({ getState, dispatch }) =>
  (next) =>
  (action) => {
    const { user } = getState()

    // solved with [ action.type !== 'user/setUser' ]
    if (user && action.type !== 'user/setUser') {
      const expDatetime = moment(user.exp * 1000)
      const tokenExpired = expDatetime < moment()

      if (!tokenExpired && moment() >= expDatetime.subtract(15, 'm')) {
        const { data } = apolloClient.mutate({ mutation: RENEW_TOKEN })
        dispatch(setUser(data.RenewToken))
      } else {
        dispatch(setUser(null))
      }
    }

    return next(action)
  }

export default autoLogin