Передача актуального state в redux-toolkit
Всем привет! Столкнулся с такой проблемой. Есть приложение на React + redux-toolkit. В бэкенде прописана логика авторизации и проверки jwt-токена. На фронте я делаю запрос на проверку пользователя. Если пришел корректный ответ, я этого пользователя диспатчу в стейт. Дальше я хочу пробросить этот стейт в дочерний компонент, что бы проверить, авторизован ли пользователь, и вывести нужную страницу, либо редирект на страницу логина. Суть проблемы в том, что в дочерний компонент пробрасывается initial state, в котором нет пользователя. Помогите решить проблему!
App.jsx
import { Routes, Route } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { setUser } from "store/slices/userSlice";
import SignIn from "./components/SignIn/SignIn";
import SignUp from "./components/SignUp/SignUp";
import Main from "./components/Main/Main";
import { useEffect } from "react";
import { checkAuth } from "api";
function App() {
const dispatch = useDispatch();
const state = useSelector((state) => state);
useEffect(() => {
if (localStorage.getItem("email")) {
checkAuth().then((data) => {
dispatch(
setUser({
email: data.email,
id: data.id,
firstName: data.firstName,
lastName: data.lastName,
phone: data.phone,
nickname: data.nickname,
description: data.description,
position: data.position,
isAuth: true,
})
);
});
}
}, []);
return (
<div className="App">
<Routes>
<Route path="/" element={<Main isAuth={state.user.isAuth} />} />
<Route path="/signin" element={<SignIn />} />
<Route path="/signup" element={<SignUp />} />
</Routes>
</div>
);
}
export default App;
Дочерний компомнент
import { Navigate } from "react-router-dom";
import { useDispatch } from "react-redux";
import { removeUser } from "store/slices/userSlice";
import Header from "components/Header/Header";
import { logout } from "api/index";
const Main = ({ isAuth }) => { //тут приходит false
const dispatch = useDispatch();
const handleLogout = () => {
logout();
dispatch(removeUser());
};
return isAuth ? (
<div>
<Header handleLogout={handleLogout} />
<h1>Welcome</h1>
<button>Log out from</button>
</div>
) : (
<Navigate to="/signin" />
);
};
export default Main;
Redux Slice
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
email: null,
id: null,
firstName: null,
lastName: null,
phone: null,
nickname: null,
description: null,
position: null,
isAuth: false,
};
const userSlice = createSlice({
name: "user",
initialState,
reducers: {
setUser(state, action) {
state.email = action.payload.email;
state.id = action.payload.id;
state.firstName = action.payload.firstName;
state.lastName = action.payload.lastName;
state.phone = action.payload.phone;
state.nickname = action.payload.nickname;
state.description = action.payload.description;
state.position = action.payload.position;
state.isAuth = action.payload.isAuth;
},
removeUser(state) {
state.email = null;
state.token = null;
state.id = null;
state.firstName = null;
state.lastName = null;
state.phone = null;
state.nickname = null;
state.description = null;
state.position = null;
state.isAuth = false;
},
},
});
export const { setUser, removeUser } = userSlice.actions;
export default userSlice.reducer;
Ответы (2 шт):
Вам не следует так прокидывать значения стейта в дочерние компоненты, которые находятся в рендер функциях роутов. Вы можете сделать все гораздо проще. Используйте useSelector в необходимом компоненте, в вашем случае это видимо Main и получите необходимое значение как только оно изменится в слайсе.
Хотел в комментарии написать, но в комментарии репутации не хватает, поэтому придется в ответе. :) Вы как-то слишком усложнили и вам в принципе верно посоветовали упростить, используя useSelector в компоненте. Еще правильнее будет сделать защищенные роуты. Оборачивая компоненты в компонент-обертку, который будет проверять логин и перенаправлять если пользователь незалогинен.
Вот так будет выглядеть защищенный роут (пример).
<Route path='profile/orders/:id'
element={
<ProtectedRoute>
<Order />
</ProtectedRoute>
} />
А вот так компонент ProtectedRoute.
import { useSelector } from "../../redux/store";
import { Navigate, useLocation } from "react-router-dom";
export function ProtectedRoute(props) {
const { children } = props;
const location = useLocation();
const { isLoggedIn } = useSelector((store) => store.auth);
if (!isLoggedIn) {
return <Navigate to="/login" state={{ from: location }} />
}
return children;
}
Удачи.