首先我是 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 组件中的代码吗?我在后端通信的函数中写了太多代码,哪里出错了?
在
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);
}
}
}
};