reactjs 我使用reduxtoolkit查询创建了一个react-redux身份验证,从节点后端获取数据,该节点后端提供一个带有accessToken的响应

zpgglvta  于 2023-04-05  发布在  React
关注(0)|答案(1)|浏览(122)

两天来我一直没有找到解决这个问题的办法。我的应用程序可以处理用户登录时的身份验证,但当一个用户注销和另一个用户登录在同一台计算机上时,重新获取数据的请求不会发送到后端,用户将使用前一个用户的数据登录。我真的不知道为什么会这样,但我在注销时清除了redux状态。我已经阅读了许多资源,但我似乎没有找到解决方案。
我尝试了一些建议性的解决方案,可以清除缓存,因为一些来源提到了缓存数据没有被清除。我已经做了很多jagons来解决这个问题,但不幸的是没有任何工作。我现在只是堆栈。
这是我的apiSlice.js代码

import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

const BASE_URL = "http://localhost:10000/api/v1"

const baseQuery = fetchBaseQuery({
    baseUrl: `${BASE_URL}/user`,
    credentials: 'include',
    prepareHeaders: (headers, { getState }) => {
        const token = getState().user.token
        console.log(token)
        if (token) {
            headers.set("Authorization", `Bearer ${token}`)
        }
        return headers
    }
})

const baseQueryWithReauth = async (args, api, extraOptions) => {
    let result = await baseQuery(args, api, extraOptions);
    console.log(result)
    if (result?.error?.originalStatus === 403) {
        //send refresh token to get new access token
        const refreshResult = await baseQuery('/token', api, extraOptions);
        console.log(refreshResult)
        if (refreshResult?.data) {
            api.dispatch(setCredentials({ ...refreshResult.data }));
            result = await baseQuery(args, api, extraOptions);
        } else {
            api.dispatch(logOut());
        }
    }

    return result;
}

export const apiSlice = createApi({
    reducerPath: 'authSlice',
    baseQuery: baseQueryWithReauth,
    tagTypes: ['User'],
    endpoints: builder => ({}),
    keepUnusedDataFor: 0,
})

这是authApi.js的代码

import { apiSlice } from "./apiSlice";
import { userApi } from "./userApi";

export const authApi = apiSlice.injectEndpoints({
    endpoints: (builder) => ({
        registerUser: builder.mutation({
            query: (user) => ({
                url: `/signup`,
                method: "POST",
                body: user
            })
        }),
        loginUser: builder.mutation({
            query: (data) => ({
                url: '/login',
                method: 'POST',
                body: data,
                credentials: 'include'
            }),
            providesTags: ['User']
        }),
        logoutUser: builder.mutation({
            query: () => ({
                url: "/logout",
                method: 'POST',
                credentials: "include"
            }),
            async onQueryStarted(args, { dispatch, queryFulfilled }) {
                try {
                    await queryFulfilled
                } catch (error) {

                }
            }
        })
    })
})
export const {
    useRegisterUserMutation,
    useLoginUserMutation,
    useLogoutUserMutation
} = authApi;

这是我的userApi.js

import { apiSlice } from "./apiSlice";

export const userApi = apiSlice.injectEndpoints({
    endpoints: (builder) => ({
        getMe: builder.query({
            query: () => ({
                url: `/me?_=${new Date().getTime()}`,
                credentials: "include",
            }),
            transformResponse: (result) => result.data.user,
            async onQueryStarted(arg, { dispatch, queryFulfilled }) {
                try {
                    const { data } = await queryFulfilled;
                    dispatch(setUser(data));
                } catch (error) { }
            },
        }),
    }),
})

export const { useGetMeQuery } = userApi

这是我的userSlice.js

import { createSlice } from '@reduxjs/toolkit';

const initialState = {
    user: null,
    token: null
};

const userSlice = createSlice({
    initialState,
    name: 'userSlice',
    reducers: {
        setCredentials: (state, action) => {
            const { accessToken, data } = action.payload
            state.token = accessToken
            state.user = data;
        },
        logout: () => initialState,
        setUser: (state, action) => {
            state.user = action.payload;
        },
    },
});

export default userSlice;

export const userActions = userSlice.actions;

这是我的store.js

import { combineReducers, configureStore } from "@reduxjs/toolkit";
import { setupListeners } from "@reduxjs/toolkit/dist/query";
import myListSlice from "./features/myListSlice";
import { persistReducer, FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER } from "redux-persist";
import storage from "redux-persist/lib/storage";
import userSlice from "./features/userSlice";
import { apiSlice } from "./api/apiSlice";

const persistConfig = {
    key: "root",
    storage: storage
}
const rootReducer = combineReducers({
    myList: myListSlice.reducer,
    user: userSlice.reducer,
    [apiSlice.reducerPath]: apiSlice.reducer,
})
const persistedReducer = persistReducer(persistConfig, rootReducer)
export const store = configureStore({
    reducer: persistedReducer,
    middleware: (getDefaultMiddleware) =>
        getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], } }).concat(apiSlice.middleware)
})

setupListeners(store.dispatch)

这是用于调度函数的Login.jsx代码

import React, { useEffect } from "react";
import { Link, useActionData, useNavigate, useSubmit } from "react-router-dom";
import Logo from "../../assets/images/Logo.svg";
import background from "../../assets/images/background.jpg";
import { useFormik } from "formik";
import { loginValidate } from "../../Validations/UserValidation";
import { useLoginUserMutation } from "../../Store/api/authApi";
import { useDispatch } from "react-redux";
import { userActions } from "../../Store/features/userSlice";
import Spinner from "../../Components/Spinner";

const Login = () => {
  // const loading = false;
  const [loginUser, { isLoading: loading }] = useLoginUserMutation();
  const submit = useSubmit();
  const navigate = useNavigate();
  const data = useActionData();
  const { setCredentials } = userActions;
  const dispatch = useDispatch();
  const formik = useFormik({
    initialValues: {
      email: "",
      password: "",
    },
    validationSchema: loginValidate,
    onSubmit: (values) => {
      submit(values, { method: "post" });
    },
  });
  useEffect(() => {
    const fetchData = async () => {
      if (data) {
        try {
          const { accessToken } = await loginUser(data).unwrap();
          dispatch(setCredentials({ accessToken, data }));
          navigate("/");
        } catch (error) {
          console.error(error);
        }
      }
    };

    fetchData();
  }, [data]);

这是我的根.jsx

import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Outlet, useNavigate } from "react-router-dom";
import Footer from "../Components/Footer";
import Navbar from "../Components/Navbar";
import Spinner from "../Components/Spinner";
import { useGetMeQuery } from "../Store/api/userApi";
import { userActions } from "../Store/features/userSlice";

function Root() {
  const { token } = useSelector((state) => state.user);
  const dispatch = useDispatch();
  const { setUser } = userActions;
  const { data: user, isLoading } = useGetMeQuery();
  const navigate = useNavigate();
  useEffect(() => {
    if (token === null) {
      navigate("/account");
    }
    dispatch(setUser(user));
  }, []);

  return (
    <>
      {isLoading ? (
        <>
          <Spinner />
        </>
      ) : (
        <div className="bg-black">
          <Navbar user={user} />
          <div className="flex flex-wrap items-center justify-between">
            <Outlet />
          </div>
          <Footer />
        </div>
      )}
    </>
  );
}

export default Root;
62o28rlo

62o28rlo1#

当您使用useGetMeQuery访问用户数据时,数据将被缓存,并且在发出请求之前,将在后续请求中从缓存中获取数据,因此它将从该高速缓存中提供数据并终止请求,但是当您使用useLazyGetMeQuery时,它有助于在组件挂载时重新获取数据,并为其触发器(在这种情况下为getUser)提供调整false。它将在重新加载和组件安装时获取用户,但删除false将使缓存成为可能。

function Root() {
  const { token, user } = useSelector((state) => state.user);
  const [isInitialLoadCompleted, setIsInitialLoadCompleted] = useState(false);
  const dispatch = useDispatch();
  const [getUser] = useLazyGetMeQuery();
  const navigate = useNavigate();
  useEffect(() => {
    if (token === null) {
      navigate("/account");
    }
    getUser(undefined, false)
      .unwrap()
      .then((response) => dispatch(setUser(response)))
      .catch(() => dispatch(logout()))
      .finally(() => setIsInitialLoadCompleted(true));
  }, [token]);

相关问题