使用 redux-toolkit asyncThunks 刷新访问令牌

问题描述 投票:0回答:1
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axiosInstance from "../utils/axiosInstance";

export const logoutUser = createAsyncThunk(
  "users/logoutUser",
  async (_, { rejectWithValue, dispatch }) => {
    try {
      const response = await axiosInstance.post("/users/logout");
      return response.data;
    } catch (error) {
      console.log(error.response.status);
      if (error.response.status === 401) {
        try {
          await dispatch(refreshUserToken());

          const retryResponse = await dispatch(logoutUser());
          return retryResponse;
        } catch (refreshError) {
          return rejectWithValue(refreshError.message);
        }
      } else {
        return rejectWithValue(error.message);
      }
    }
  }
);

export const refreshUserToken = createAsyncThunk(
  "users/refreshUserToken",
  async (_, { rejectWithValue }) => {
    try {
      const response = await axiosInstance.post("/users/refresh-token");
      console.log("refreshToken->", response.data);
      return response.data;
    } catch (error) {
      return rejectWithValue(error.message);
    }
  }
);

const initialState = {
  user: {},
  status: "idle",
  error: null,
};

const userSlice = createSlice({
  name: "user",
  initialState,
  reducers: {},
  extraReducers(builder) {
    builder
      .addCase(logoutUser.pending, (state, action) => {
        state.status = "loading";
      })
      .addCase(logoutUser.fulfilled, (state, action) => {
        state.status = "success";
        console.log("case f logout->", action.payload); // log statement
        state.user = {};
      })
      .addCase(logoutUser.rejected, (state, action) => {
        state.error = action.payload;
      })
      .addCase(refreshUserToken.pending, (state, action) => {
        state.status = "loading";
      })
      .addCase(refreshUserToken.fulfilled, (state, action) => {
        state.status = "success";
        console.log("case f refreshToken->", action.payload); // log statement
      })
      .addCase(refreshUserToken.rejected, (state, action) => {
        state.error = action.payload;
      });
  },
});

export const getUserState = (state) => state.user.status;
export const getUserError = (state) => state.user.error;
export const getUser = (state) => state.user.user;

export const {} = userSlice.actions;

export default userSlice.reducer;

下面是我的 axios 实例。

const axiosInstance = axios.create({
    baseURL: "http://localhost:8082/api",
    withCredentials: true
});

我是 Redux-Toolkit 的新手,所以我很困惑。在

logoutUser
asyncThunk 中,我检查收到的错误代码是否为 401(访问令牌已过期),然后通过调用
refreshUserToken
thunk 内的
logoutUser
thunk 来请求刷新令牌,但是这导致 logoutThunk 运行两次。 我该如何纠正这个问题?或者是否有更好的方法来使用 asyncThunks 处理令牌刷新?

注意:我使用 cookie 来发送和接收来自服务器的访问和刷新令牌。

javascript reactjs redux axios redux-toolkit
1个回答
0
投票

您可以重新调用

just
端点,而不是重新分派 logoutUser 运行 Thunk 操作至少一次的操作。

示例:

export const logoutUser = createAsyncThunk(
  "users/logoutUser",
  async (_, { rejectWithValue, dispatch }) => {
    try {
      const { data } = await axiosInstance.post("/users/logout");
      return data;
    } catch(error) {
      console.log(error.response.status);
      if (error.response.status === 401) {
        try {
          // Attempt token refresh, unwrap result
          await dispatch(refreshUserToken()).unwrap();

          // Retry original request
          const { data } = await axiosInstance.request(error.config);;
          return data;
        } catch(refreshError) {
          return rejectWithValue(refreshError.message);
        }
      } else {
        return rejectWithValue(error.message);
      }
    }
  }
);

为了避免到处实现这种重试行为,您可以尝试使用响应拦截器来抽象和处理重试逻辑。

示例:

../utils/axiosInstance

import axios from "axios";
import { store } from "../path/to/store";

const axiosInstance = axios.create({
  baseURL: "http://localhost:8082/api",
  withCredentials: true
});

axiosInstance.interceptors.response.use(
  response => response,
  async error => {
    if (error.response.status === 401) {
      // Attempt token refresh, unwrap result
      await store.dispatch(refreshUserToken()).unwrap();

      // Retry original request
      return axiosInstance.request(error.config);
    }
    return Promise.reject(error);
  },
);

export default axiosInstance;
export const logoutUser = createAsyncThunk(
  "users/logoutUser",
  async (_, { rejectWithValue }) => {
    try {
      const { data } = await axiosInstance.post("/users/logout");
      return data;
    } catch(error) {
      return rejectWithValue(error.message);
    }
  }
);
© www.soinside.com 2019 - 2024. All rights reserved.