中间件中的无限循环调度动作
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
我正在尝试创建一个刷新登录中间件(在即将过期时获取一个新的 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