如何在 react redux 中添加受保护的路由
Posted
技术标签:
【中文标题】如何在 react redux 中添加受保护的路由【英文标题】:How to add protected routes in react redux 【发布时间】:2021-05-18 05:23:10 【问题描述】:我创建了一个登录组件,其中包含所有逻辑内容。
登录reducer是:
const openState =
loggedIn: null,
user: null
export default (state = openState, action) =>
switch (action.type)
case LOGIN:
return ...state, loggedIn: true, user: action.payload
case LOGOUT:
return ...state, loggedIn: false, user: null
default:
return openState
行动:
export const logIn = (user) =>
return
type: LOGIN,
payload: user
export const logOut = () =>
return
type: LOGOUT
一切正常,但我不确定如何将 loggedIn
和 user
道具从操作传递到路由组件以保护所有路由:
const MainRoutes = props =>
const loggedIn = props;
console.log(props.loggedIn)
return (
<Router history=history>
<Baseline />
<Menu/>
<Container maxWidth="md">
<Switch>
<Route exact path="/Login" component=Login />
<Route exact path="/Carousel" component=Carousel />
<Route exact path="/Stepper" component=Stepper />
<Route component=NotFound />
</Switch>
</Container>
</Router>
);
const mapStateToProps = (state) =>
return loggedIn: state.loggedIn ;
;
export default connect(mapStateToProps)(MainRoutes);
如果我控制台记录loggedIn
道具,我会得到未定义的:|
基于loggedIn
,我可以在路由组件中创建逻辑。
【问题讨论】:
您在mapStateToProps
内有控制台state.loggedIn
吗?它返回什么?
@sayalok 返回未定义,但 state.auth. loggedIn
返回默认状态 null
。在登录组件中为 true,但在任何其他组件中返回 null
您是否将操作导入到主文件中,例如 app.js
?
@sayalok 不,不是因为我没有在 app.js 中使用它们
如果您不导入您的操作,您将如何找到loggedIn
?
【参考方案1】:
这种方式使用react router官方的例子,基本上你需要把你的redux store和这个ProtectedRoute组件连接起来
import connect from "react-redux";
import Route, Redirect from "react-router-dom";
function PrivateRoute( children, loggedIn, ...rest )
return (
<Route
...rest
render=( location ) =>
loggedIn ? (
children
) : (
<Redirect
to=
pathname: "/login",
state: from: location
/>
)
/>
);
const mapStateToProps = (state) =>
return loggedIn: state.auth.loggedIn ;
;
export default connect(mapStateToProps)(PrivateRoute);
如您所见,您正在将 loggedIn 值传递给这个新的路由组件,所以如果它为 false,它会将您返回到登录页面。
要使用它,只需调用路由器中的组件:
import PrivateRoute from "./PrivateRoute";
const MainRoutes = () =>
return (
<Router history=history>
<Baseline />
<Menu/>
<Container maxWidth="md">
<Switch>
<Route exact path="/Login" component=Login />
<PrivateRoute exact path="/Carousel" component=Carousel />
<PrivateRoute exact path="/Stepper" component=Stepper />
<Route component=NotFound />
</Switch>
</Container>
</Router>
);
这是一个很好的方法,因为您没有将 MainRoutes 组件连接到商店,在我看来,这不是您在该组件中需要的东西,您最终会在 Routes 内的组件中使用它。
另外,您忘记将 state.auth.loggedIn 添加到您的 Main Routes mapStateToProps,这就是您尝试使用它时未定义的原因。
这是我用来说明该过程的一个小沙盒: https://codesandbox.io/s/unruffled-keller-pwlib
【讨论】:
【参考方案2】:您可以简单地定义一个自定义的ProtectedRoute
组件,该组件将连接到 redux 状态。在您的情况下,它应该将 state.auth.loggedIn
和 state.auth.user
映射到道具并执行 Redirect
如果这些值是虚假的:
import React from "react";
import Route, Redirect from "react-router-dom";
import PropTypes from "prop-types";
const ProtectedRoute = (props) =>
const redirectPath, component, user, loggedIn, ...routeProps = props;
const Component = component;
const isAccessible = Boolean(user) && loggedIn;
return (
<Route
...routeProps
render=props =>
if (isAccessible) return <Component ...props />;
return <Redirect to= pathname: redirectPath || "/Login" />;
/>
);
;
ProtectedRoute.propTypes =
path: PropTypes.string.isRequired,
redirectPath: PropTypes.string,
component: PropTypes.oneOfType([
PropTypes.shape( render: PropTypes.func.isRequired ),
PropTypes.func
]),
;
const mapStateToProps = (state) =>
return
loggedIn: state.auth.loggedIn,
user: state.auth.user
;
;
export default connect(mapStateToProps)(ProtectedRoute);
有了这个,您的MainRoutes
将不再需要connect
:
const MainRoutes = props =>
return (
<Router history=history>
...
<Container maxWidth="md">
<Switch>
<Route exact path="/Login" component=Login />
<ProtectedRoute exact path="/Carousel" component=Carousel />
<ProtectedRoute exact path="/Stepper" component=Stepper />
<Route component=NotFound />
</Switch>
</Container>
</Router>
);
export default MainRoutes;
更新:
如果你想在页面刷新后保持auth
状态,你需要对你的 redux 存储进行一些额外的设置。主要思想是订阅每个操作,并将新的状态副本放入localStorage
或cookies
,并在您的应用启动之前从选定的存储中持久保存initialState
。这是一个例子:
function getFromStorage(key)
try
return JSON.parse(window.localStorage.getItem(key)) || ;
catch (err)
return ;
const initialState = getFromStorage("APP_STATE");
const store = createStore(
rootReducer,
initialState, // should go after root reducer
...
);
store.subscribe(() =>
window.localStorage.setItem("APP_STATE", JSON.stringify(store.getState()));
);
这是工作的sandbox(由SuleymanSah 引导)
【讨论】:
使用render
属性而不是 component
对性能来说要差得多。如果使用此方法,请务必实现类似memo
的性能优化。
@Slbox 你有这个来源吗?我不明白为什么会有显着差异。
@deckele medium.com/@migcoder/…
@Slbox 感谢来源。但是,在您提供的文章中,作者声称Route组件的component
或render
prop在性能上没有区别。
我想我应该读过结论。 Here's a Stack answer on the topic.【参考方案3】:
您可以创建一个 Guard,它从 Redux 存储获取登录状态并在登录时呈现子级,否则重定向到其他地方。
要从 store 中获取 state,可以使用 useSelector
React Hook。
import useSelector from 'react-redux';
function AuthGuard(children)
const loggedIn = useSelector(store => store.loggedIn);
if (loggedIn)
return children;
else
return <Redirect to="/login" />
在路上
<Container maxWidth="md">
<Switch>
<Route exact path="/Login" component=Login />
<AuthGuard>
<Route exact path="/Carousel" component=Carousel />
<Route exact path="/Stepper" component=Stepper />
</AuthGuard>
<Route component=NotFound />
</Switch>
</Container>
【讨论】:
【参考方案4】:正如其他人已经描述的那样,您最好创建一条受保护的路线来完成您想要的。如果他/她没有登录,您只需将用户重定向到登录路由。
这是我的实现:(codesandbox)
import React from "react";
import Route, Redirect from "react-router-dom";
import connect from "react-redux";
const ProtectedRoute = (
path,
component: Component,
render,
loggedIn,
...rest
) =>
return (
<Route
path=path
...rest
render=(props) =>
if (loggedIn)
return Component ? <Component ...props /> : render(props);
return (
<Redirect
to=
pathname: "/login",
state: from: props.location
/>
);
/>
);
;
const mapStateToProps = (state) =>
const loggedIn = state.auth;
return
loggedIn
;
;
export default connect(mapStateToProps)(ProtectedRoute);
【讨论】:
问题是我已经使用了你的例子,但并没有真正理解为什么在浏览器刷新时路由变得未知,不知道为什么:| @RulerNature redux 状态在浏览器刷新时丢失,这是正常的。你可以查看 redux-persist 包。 github.com/rt2zz/redux-persist【参考方案5】:下面的代码对我有用。
const SecretRouter = ( component: Component, location, loggedIn , ...rest ) => (
<Route
location=location
...rest
render=(props) =>
loggedIn === true ? (
<Component ...props />
) : (
<Redirect
to=
pathname: "/Login"
/>
)
/>
);
const mapStateToProps = (state) =>
return loggedIn: state.loggedIn ;
;
export const PrivateRouter = connect(mapStateToProps)(
SecretRouter
);
const MainRoutes = props =>
return (
<Router history=history>
<Baseline />
<Menu/>
<Container maxWidth="md">
<Switch>
<Route exact path="/Login" component=Login />
<PrivateRouter exact path="/Carousel" component=Carousel />
<PrivateRouter exact path="/Stepper" component=Stepper />
<Route component=NotFound />
</Switch>
</Container>
</Router>
);
export default MainRoutes;
【讨论】:
【参考方案6】:在这种情况下我要做的基本上是创建一个私有路由,只有在您登录时才能访问,或者在您的情况下,如果您的 loggedIn: state.loggedIn
中的登录为 true 并且用户存在于state.auth.user
。如果两者都是错误的,它会将您重定向到 /login
页面以在访问这些私有路由之前先登录或注册。
所以要创建一个私有路由或<PrivateRoute />
,
import React from 'react'; import Route, Redirect from 'react-router-dom'; import PropTypes from 'prop-types'; import connect from 'react-redux'; const PrivateRoute = ( component: Component, auth: user, loggedIn , ...rest ) => ( <Route ...rest render=props => loggedIn && Boolean(user) ? ( <Component ...props /> ) : ( <Redirect to="/login" /> ) /> ); PrivateRoute.propTypes = auth: PropTypes.object.isRequired const mapStateToProps = state => ( auth: state.auth ); export default connect(mapStateToProps)(PrivateRoute);
这将为您创建该私有路由或<PrivateRoute />
,您可以将其导入您的MainRoutes
文件,然后您可以将应该受私有路由保护的组件传递给它。
const MainRoutes = props => return ( <Router history=history> <Baseline /> <Menu/> <Container maxWidth="md"> <Switch> <Route exact path="/Login" component=Login /> <PrivateRoute exact path="/Carousel" component=Carousel /> <PrivateRoute exact path="/Stepper" component=Stepper /> <Route component=NotFound /> </Switch> </Container> </Router> ); export default MainRoutes;
这样,要保护的组件将检查用户是否存在以及您是否已登录,如果为真,它将允许您访问这些组件。
【讨论】:
以上是关于如何在 react redux 中添加受保护的路由的主要内容,如果未能解决你的问题,请参考以下文章
即使我在 react/redux 中使用受保护的路由设置了身份验证,我仍然会被注销?当我刷新页面时