RTK查询拦截器循环依赖

Posted

技术标签:

【中文标题】RTK查询拦截器循环依赖【英文标题】:RTK Query Interceptor Circular Dependency 【发布时间】:2022-01-13 15:55:53 【问题描述】:

试图找出实现以下目标的最佳方法:

    API 通过createApi 通过createSlice 验证切片 API 收到 401 时清除 redux 身份验证状态。

前两个没问题!但问题是当我需要向 API 添加拦截器时(无效的身份验证 - 清除本地身份验证状态):

// api.ts
import authSlice from './authSlice';
const baseQueryWithReauth: BaseQueryFn<string | FetchArgs, unknown, FetchBaseQueryError> = async (
  args,
  api,
  extraOptions
) => 
  const result = await baseQuery(args, api, extraOptions)

  if (result.error?.status === 401) 
    api.dispatch(authSlice.actions.clearAuth())
  

  return result


export const api = createApi(
  reducerPath: API_REDUCER_KEY,
  baseQuery: baseQueryWithReauth,
  endpoints: () => (),
)
// authService.ts
import  User  from '../../models/User'
import  api  from '../api'

export const API_REDUCER_KEY = 'api'

interface AuthResponse 
  user: User
  jwt: string


interface LoginData 
  email: string
  password: string


export const authApi = api.injectEndpoints(
  endpoints: (builder) => (
    login: builder.mutation<AuthResponse, LoginData>(
      query: (body) => 
        return 
          url: 'login',
          method: 'POST',
          body,
        
      ,
    ),
  ),
)

export const  useLoginMutation  = authApi

这会导致循环依赖 - 因为身份验证切片需要为身份验证功能(登录、注销等)调用 API:

// authSlice.ts
import  api  from './api';
...
export const loginLocal = createAsyncThunk<
  Pick<AuthState, 'user' | 'accessToken'>,
  
    email: string
    password: string
  
>('auth/login', async ( email, password ,  dispatch ) => 
  const response = await dispatch(
    authApi.endpoints.login.initiate(
      email,
      password,
    )
  )

  if ('error' in response) 
    if ('status' in response.error) 
      throw new Error(response.error.data as string)
    
    throw new Error(response.error.message)
  

  const  data  = response

  const  jwt: accessToken, user  = data

  return  user, accessToken 
)

循环依赖如下:authSlice -> authService -> api -> authSlice

有没有办法解决这个问题 - 或者我可以使用更好/不同的模式?

【问题讨论】:

如果你把baseQueryWithReauth移到一个单独的文件里会不会解决问题?比你不必在你的api.ts 中导入authSlice 另外,为什么loginLocal 写成createAsyncThunk?这感觉就像会进入 API 切片一样。 @TheWuif - 我相信这仍然是一个循环依赖,只是在链中有一个额外的链接:authSlice -> authService -> api -> baseQueryWithReauth -> authSlice @markerikson - 我也在该函数中应用一些与问题无关的异步逻辑:) 【参考方案1】:

一个简单的解决方案是简单地将“clearAuth”操作拉到它自己的文件中 - 按照https://redux-toolkit.js.org/usage/usage-guide#exporting-and-using-slices:

这通常需要将共享代码提取到两个模块都可以导入和使用的单独公共文件中。在这种情况下,您可以使用 createAction 在单独的文件中定义一些常见的动作类型,将这些动作创建者导入每个切片文件,并使用 extraReducers 参数处理它们。

// clearAuthAction.ts
import  createAction  from '@reduxjs/toolkit'

export const CLEAR_AUTH = 'auth/clearAuth'

export const clearAuth = createAction<void>(CLEAR_AUTH)

然后在authSlice中使用builder:

// authSlice.ts
builder.addCase(CLEAR_AUTH, (state) => 
  state.user = null
  state.accessToken = null
)

并在 API 中调度 clearAuth:

// api.ts
const baseQueryWithReauth: BaseQueryFn<string | FetchArgs, unknown, FetchBaseQueryError> = async (
  args,
  api,
  extraOptions
) => 
  const result = await baseQuery(args, api, extraOptions)

  if (result.error?.status === 401 || result.error?.status === 403) 
    api.dispatch(clearAuth())
  

  return result

【讨论】:

以上是关于RTK查询拦截器循环依赖的主要内容,如果未能解决你的问题,请参考以下文章

拦截器报循环依赖错误

将 $state (ui-router) 注入 $http 拦截器会导致循环依赖

具有循环依赖的 GraphQL 设计

Spring Security 循环 bean 依赖

精通Mybatis之结果集处理流程与映射体系(重点mybatis嵌套子查询,循环依赖解决方案)

如何使用 GraphQL Relay 连接修复循环依赖