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

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

  1. API 通过createApi
  2. 通过createSlice 验证切片
  3. 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 - 我也在该函数中应用一些与问题无关的异步逻辑:)

标签: reactjs redux redux-toolkit rtk-query


【解决方案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
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-13
    • 2016-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-12
    相关资源
    最近更新 更多