Redux 向后端发送请求时发生钩子问题

问题描述 投票:0回答:1

首先我是 redux 的新手。我正在尝试创建电子商务网站。在后端,我使用 spring 在下面的代码中处理登录。

@PostMapping("/login")
    public ResponseEntity<AuthResponse> authenticate(
            @RequestBody LoginRequest request
    ){
        return ResponseEntity.ok(authService.authenticate(request));
    }
public AuthResponse authenticate(LoginRequest request) {
        authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        request.getEmail(),
                        request.getPassword()
                )
        );
        var user = userRepository.findByEmail(request.getEmail())
                .orElseThrow();
        var jwtToken = jwtService.generateToken(user);
        return AuthResponse.builder()
                .token(jwtToken)
                .build();
    }

当用户登录时,我可以通过 JWT 访问他们的电子邮件。 然而,我的挑战从这里开始。登录后,我在 React 中只有用户的电子邮件,并且我需要使用此电子邮件检索用户的数据。 在 React 中,我尝试将登录用户的电子邮件发送到后端以访问他们的数据,但我遇到了与前端错误相关的错误。

Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)

我的react-redux代码与错误相关,

const handleSubmit = async () => {
        try {
            const userData = await login({ email: formikSignIn.values.signEmail , password: formikSignIn.values.signPassword }).unwrap();
            const data = await useGetLoggedUserQuery(formikSignIn.values.signEmail);
            console.log(data);
            dispatch(setCredentials({ ...userData, email: formikSignIn.values.signEmail }));
            setIsModalLoggedInOpen(true);
            setTimeout(() => {
                navigate('/home');
            }, 2000);
        } catch (err: any) {
            console.log(err.status);
            if (err.status === "FETCH_ERROR") {
                showErrorModal("No server response!", "Server is under maintenance, please try again later.");
            } else {
                if (err.status === 403) {
                    showErrorModal("Wrong email or password", "Please check your login informations again.");
                } else {
                    showErrorModal("Login failed!", "Please try again.");
                    console.log(err);
                }
            }
        }
    }
export type UserState = {
    id: number;
    firstName: string;
    lastName: string;
    email: string;
    address?: string;
    role: Role;
  };

export type AuthState = {
    isAuthenticated: boolean;
    user?: UserState; 
    token?: string;
};
  

enum Role {
    ADMIN = "ADMIN",
    USER = "USER",
}
export const authApiSlice = apiSlice.injectEndpoints({
    endpoints: builder => ({
        login: builder.mutation({
            query: credentials => ({
                url: '/auth/login',
                method: 'POST',
                body: { ...credentials }
            })
        }),
        getLoggedUser: builder.query({
            query: userEmail => `/user/${userEmail}`,  
        }),
    })
})

export const {
    useLoginMutation,
    useGetLoggedUserQuery 
} = authApiSlice
export const store = configureStore({
    reducer: {
        [apiSlice.reducerPath]: apiSlice.reducer,
        auth: authReducer
    },
    middleware: getDefaultMiddleware =>
        getDefaultMiddleware().concat(apiSlice.middleware),
    devTools: true
})
const baseQuery = fetchBaseQuery({
    baseUrl: 'http://localhost:8080',
    credentials: 'include',
    prepareHeaders: (headers, { getState }) => {
        const state = getState() as AuthState;
        const token = state.token;
        if (token) {
            headers.set("authorization", `Bearer ${token}`);
        }
        return headers;
    }
})

const baseQueryWithReauth = async (args: any, api: any, extraOptions: any) => {
    let result = await baseQuery(args, api, extraOptions);

    if (result?.error && 'originalStatus' in result.error){
        if (result?.error?.originalStatus === 403) {
            console.log('sending refresh token');
            const refreshResult = await baseQuery('/refresh', api, extraOptions);
            console.log(refreshResult);
            if (refreshResult?.data) {
                const user = api.getState().user;
                api.dispatch(setCredentials({ ...refreshResult.data, user }));
                result = await baseQuery(args, api, extraOptions);
            } else {
                api.dispatch(logOut({}));
            }
        }
    }
    return result;
}

export const apiSlice = createApi({
    baseQuery: baseQueryWithReauth,
    endpoints: builder => ({})
})

此外,将 React 与 Redux 结合使用难道不会减少 React 组件中的代码吗?我在后端通信的函数中写了太多代码,哪里出错了?

reactjs spring redux
1个回答
0
投票

useGetLoggedUserQuery
回调中调用
handleSubmit
钩子会破坏 React 的 Hooks 规则

仅调用顶层的钩子

不要在循环、条件或嵌套函数内调用 Hooks。相反,始终在 React 函数的顶层使用 Hooks, 在任何提前返回之前。通过遵循此规则,您可以确保 每次组件渲染时都会以相同的顺序调用钩子。 这就是 React 能够正确保存 Hooks 状态的原因 在多个

useState
useEffect
调用之间。

我建议您导出并使用Lazy Query Hook,它返回可在回调中使用的触发函数。

示例:

export const authApiSlice = apiSlice.injectEndpoints({
  endpoints: builder => ({
    ....
    getLoggedUser: builder.query({
      query: userEmail => `/user/${userEmail}`,  
    }),
  })
})

export const {
  useLoginMutation,
  useGetLoggedUserQuery,
  useLazyGetLoggedUserQuery, // <-- lazy query hook
} = authApiSlice
import { useLazyGetLoggedUserQuery } from '../path/to/apiSlice';

...

const [getLoggedUser] = useLazyGetLoggedUserQuery();

...

const handleSubmit = async () => {
  try {
    const userData = await login({
      email: formikSignIn.values.signEmail,
      password: formikSignIn.values.signPassword
    }).unwrap();

    const data = await getLoggedUser(formikSignIn.values.signEmail).unwrap();

    console.log(data);
    dispatch(setCredentials({
      ...userData,
      email: formikSignIn.values.signEmail
    }));
    setIsModalLoggedInOpen(true);
    setTimeout(() => {
      navigate('/home');
    }, 2000);
  } catch (err: any) {
    console.log(err.status);
    if (err.status === "FETCH_ERROR") {
      showErrorModal(
        "No server response!",
        "Server is under maintenance, please try again later."
      );
    } else {
      if (err.status === 403) {
        showErrorModal(
          "Wrong email or password",
          "Please check your login informations again."
        );
      } else {
        showErrorModal("Login failed!", "Please try again.");
        console.log(err);
      }
    }
  }
};
© www.soinside.com 2019 - 2024. All rights reserved.