我正在使用 react-router 链接到不同的页面。一切正常,但是,一旦我刷新页面,它会进入登录页面一会儿,然后返回主页。更糟糕的是,如果我转到管理页面,刷新页面会将用户定向到登录页面,但是,用户仍然登录并且只显示登录页面。我还在使用 Firebase Firestore 和 firebase 身份验证。
应用程序.js
const App = (props) => {
const { setCurrentUser, currentUser } = props;
const admin = checkUserAdmin(currentUser);
console.log(admin);
useEffect(() => {
const authListener = auth.onAuthStateChanged(async (userAuth) => {
if (userAuth) {
const userRef = await handleUserProfile(userAuth);
userRef.onSnapshot((snapshot) => {
setCurrentUser({
id: snapshot.id,
...snapshot.data(),
});
});
}
setCurrentUser(userAuth);
});
return () => {
authListener();
};
}, []);
return (
<div className="App">
<Switch>
<Route
exact
path="/login"
render={() => (
<MainLayout>
<LoginPage />
</MainLayout>
)}
/>
<Route
exact
path="/profile"
render={() => (
<WithAuth>
<MainLayout>
<ProfilePage />
</MainLayout>
</WithAuth>
)}
/>
<Route
exact
path="/admin"
render={() => (
<WithAdmin>
<AdminHome />
</WithAdmin>
)}
/>
</Switch>
</div>
);
};
const mapStateToProps = ({ user }) => ({
currentUser: user.currentUser,
});
const mapDispatchToProps = (dispatch) => ({
setCurrentUser: (user) => dispatch(setCurrentUser(user)),
});
export default connect(mapStateToProps, mapDispatchToProps)(App);
withAuth - 限制页面的用户。如果 currentUser 是 guest 用户,它将用户定向到登录页面。
import { useAuth } from "./../custom-hooks";
import { withRouter } from "react-router-dom";
const WithAuth = (props) => useAuth(props) && props.children;
export default withRouter(WithAuth);
useAuth - 限制页面的用户。如果 currentUser 是 guest 用户,它将用户定向到登录页面。
const mapState = ({ user }) => ({
currentUser: user.currentUser,
});
const useAuth = (props) => {
const { currentUser } = useSelector(mapState);
useEffect(() => {
if (!currentUser) {
props.history.push("/login");
}
}, [currentUser]);
return currentUser;
};
export default useAuth;
withAdmin - 只有管理员可以访问的页面
import { useAdmin } from "../../custom-hooks";
const WithAdmin = (props) => useAdmin(props) && props.children;
export default WithAdmin;
useAdmin - 页面只能由管理员访问。如果用户不是管理员,它会将用户定向到登录页面。
const mapState = ({ user }) => ({
currentUser: user.currentUser,
});
const useAdmin = (props) => {
const { currentUser } = useSelector(mapState);
const history = useHistory();
useEffect(() => {
if (!checkUserAdmin(currentUser)) {
history.push("/login");
}
}, [currentUser]);
return currentUser;
};
export default useAdmin;
下面是我的index.js
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
reducer : 用户类型:
const userTypes = {
SET_CURRENT_USER: "SET_CURRENT_USER",
};
export default userTypes;
用户操作:
import userTypes from "./user.types";
export const setCurrentUser = (user) => ({
type: userTypes.SET_CURRENT_USER,
payload: user,
});
用户 reducer :
import userTypes from "./user.types";
const INITIAL_STATE = {
currentUser: null,
};
const userReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case userTypes.SET_CURRENT_USER:
return {
...state,
currentUser: action.payload,
};
default:
return state;
}
};
export default userReducer;
根 reducer :
import { combineReducers } from "redux";
import userReducer from "./user/user.reducer";
export default combineReducers({
user: userReducer,
});
商店.js
import { createStore, applyMiddleware } from "redux";
import logger from "redux-logger";
import rootReducer from "./rootReducer";
export const middlewares = [logger];
export const store = createStore(rootReducer, applyMiddleware(...middlewares));
export default store;
检查用户管理.js
export const checkUserAdmin = (currentUser) => {
if (!currentUser || !Array.isArray(currentUser.roles)) return false;
const { roles } = currentUser;
if (roles.includes("admin")) return true;
return false;
};
最佳答案
我建议向您的 userReducer
添加一个 authPending
状态,最初是 true
,并且在 firestore 逻辑处理用户更改时设置/清除.
userReducer 和 Action
const userTypes = {
SET_AUTH_PENDING: "SET_AUTH_PENDING",
SET_CURRENT_USER: "SET_CURRENT_USER",
};
const setAuthPending = pending => ({
type: userTypes.SET_AUTH_PENDING,
payload: pending,
});
const INITIAL_STATE = {
authPending: true,
currentUser: null,
};
const userReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case userTypes.SET_CURRENT_USER:
return {
...state,
authPending: false
currentUser: action.payload,
};
case userTypes.SET_AUTH_PENDING:
return {
...state,
authPending: action.payload,
};
default:
return state;
}
};
应用程序.js
const App = (props) => {
const {
setAuthPending, // <-- access action
setCurrentUser,
currentUser
} = props;
const admin = checkUserAdmin(currentUser);
console.log(admin);
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(async (userAuth) => {
setAuthPending(true); // <-- start auth pending
if (userAuth) {
const userRef = await handleUserProfile(userAuth);
userRef.onSnapshot((snapshot) => {
setCurrentUser({ // <-- will clear auth pending
id: snapshot.id,
...snapshot.data(),
});
});
} else {
setCurrentUser(null); // <-- clear user data and pending
}
});
return () => {
unsubscribe();
};
}, []);
return (
<div className="App">
<Switch>
...
</Switch>
</div>
);
};
const mapStateToProps = ({ user }) => ({
currentUser: user.currentUser,
});
const mapDispatchToProps = {
setAuthPending, // <-- wrap action creator in call to dispatch
setCurrentUser,
};
钩子(Hook)和包装器
对于这些,我建议将逻辑抽象到自定义 Route
组件中。
const AuthRoute = props => {
const { authPending, currentUser } = useSelector(state => state.user);
if (authPending) {
return "Loading..."; // or maybe a loading spinner
};
return currentUser ? (
<Route {...props} />
) : (
<Redirect to="/login" />
);
};
const AdminRoute = props => {
const { authPending, currentUser } = useSelector(state => state.user);
if (authPending) {
return "Loading..."; // or maybe a loading spinner
};
return checkUserAdmin(currentUser) ? (
<Route {...props} />
) : (
<Redirect to="/login" />
);
};
然后路线变成
<Switch>
<Route
exact
path="/"
render={() => (
<MainLayout>
<Homepage />
</MainLayout>
)}
/>
<Route
exact
path="/login"
render={() => (
<MainLayout>
<LoginPage />
</MainLayout>
)}
/>
<AuthRoute
exact
path="/profile"
render={() => (
<MainLayout>
<ProfilePage />
</MainLayout>
)}
/>
<AdminRoute
exact
path="/admin"
component={AdminHome}
/>
</Switch>
在此之后,您可能需要研究将 redux 状态保存到 localStorage 中,并在实例化 store
时从 localStorage 重新填充 redux 状态(preloadedState
parameter) 对象在您的应用加载时。您可以自己管理或查看类似 redux-persist 的内容.
关于javascript - React - 即使刷新了页面,如何保持在同一页面上?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68842442/