反应路由器私有路由/重定向不起作用
Posted
技术标签:
【中文标题】反应路由器私有路由/重定向不起作用【英文标题】:React router private routes / redirect not working 【发布时间】:2017-09-17 03:29:08 【问题描述】:我稍微调整了 React Router 示例,以使私有路由与 Redux 配合得很好,但是在链接或重定向到其他“页面”时不会呈现任何组件。这个例子可以在这里找到:
https://reacttraining.com/react-router/web/example/auth-workflow
他们的 PrivateRoute 组件如下所示:
const PrivateRoute = ( component: Component, ...rest ) => (
<Route ...rest render=props => (
fakeAuth.isAuthenticated ? (
<Component ...props/>
) : (
<Redirect to=
pathname: '/login',
state: from: props.location
/>
)
)/>
)
但是,因为我已经将它合并到了一个 Redux 应用程序中,所以我不得不稍微调整一下 PrivateRoute,以便我可以访问 redux 存储以及路由 Props:
const PrivateRouteComponent = (props) => (
<Route ...props.routeProps render=() => (
props.logged_in ? (
<div>props.children</div>
) : (
<Redirect to=
pathname: '/login',
state: from: props.location
/> )
) />
);
const mapStateToProps = (state, ownProps) =>
return
logged_in: state.auth.logged_in,
location: ownProps.path,
routeProps:
exact: ownProps.exact,
path: ownProps.path
;
;
const PrivateRoute = connect(mapStateToProps, null)(PrivateRouteComponent);
export default PrivateRoute
每当我没有登录并点击 PrivateRoute 时,我都会正确地重定向到 /login。但是,在登录并使用<Redirect .../>
或单击任何<Link ...>
到 PrivateRoute 后,URI 会更新,但视图不会。它停留在同一个组件上。
我做错了什么?
补图,在app的index.js
里面有一些不相关的东西,路线是这样设置的:
ReactDOM.render(
<Provider store=store>
<App>
<Router>
<div>
<PrivateRoute exact path="/"><Home /></PrivateRoute>
// ... other private routes
<Route path="/login" component=Login />
</div>
</Router>
</App>
</Provider>,
document.getElementById('root')
);
【问题讨论】:
【参考方案1】:你需要用<Switch>
标签包裹你的Route
ReactDOM.render(
<Provider store=store>
<App>
<Router>
<div>
<Switch>
<PrivateRoute exact path="/"><Home /></PrivateRoute>
// ... other private routes
<Route path="/login" component=Login />
</Switch>
</div>
</Router>
</App>
</Provider>,
document.getElementById('root'));
【讨论】:
你必须让组件重新渲染。它也可以将 PrivateRoute 设置为不纯:export default connect(mapStateToProps, null, null, pure: false )(PrivateRoute);
另请参阅Stack Overflow
我试图在与withRouter
连接的组件内部使用props.history.push
,而我的PrivateRoute
在没有Switch
的情况下无法工作。为我节省了很多时间,谢谢!【参考方案2】:
设置私有路由不纯:
export default connect(mapStateToProps, null, null,
pure: false,
)(PrivateRoute);
这将使组件重新渲染。
请看:react-router-4-x-privateroute-not-working-after-connecting-to-redux。
【讨论】:
【参考方案3】:刚刚遇到同样的问题,我通过制作我的 App redux 容器并将 isAuthenticated 作为道具传递给 PrivateRoute 解决了这个问题
就是这样,希望对你有帮助
const App = (props) =>
return (
<Provider store=store>
<Router>
<div>
<PrivateRoute path="/secured" component=Secured isAuthenticated=props.isAuthenticated />
</div>
</Router>
</Provider>
);
;
const mapStateToProps = state => (
isAuthenticated: state.isAuthenticated
);
export default connect(mapStateToProps)(App);
然后在我的 PrivateRoute 中
const PrivateRoute = ( component: Component, isAuthenticated, ...rest) => (
<Route
...rest
render=props => (
isAuthenticated
? (
<Component ...props />
)
: (<Redirect to= pathname: '/login', state: from: props.location />)
)
/>
);
export default PrivateRoute;
【讨论】:
谢谢,稍后我会调查的! :-D 我也有类似的问题。事实上,我不想将 isAuthenticated 传递给 PrivateRoute 的每次使用,这就是我连接到 redux 的原因,但令人惊讶的是,直到任何额外的 prop 传递给我设法使用rest
参数来访问来自mapStateToProps
的数据:
const PrivateRoute = (component: Component, ...rest) =>
const isAuthenticated = rest;
return (
<Route ...rest render=props => (
isAuthenticated ? (
<Component ...props/>
) : (
<Redirect to=
pathname: '/login',
state: from: props.location
/>
)
)
/>
);
;
PrivateRoute.propTypes =
isAuthenticated: PropTypes.bool.isRequired,
;
function mapStateToProps(state)
return
isAuthenticated: state.user.isAuthenticated,
;
export default connect(mapStateToProps)(PrivateRoute);
【讨论】:
我必须这样做,并在路由中添加<Switch>
以使其工作。【参考方案5】:
嗯,我觉得这个问题的答案真的应该更详细,所以我在这里,经过 4 小时的挖掘。
当你用 connect() 包装你的组件时,React Redux 会在其上实现 shouldComponentUpdate(如果你在 github 问题上搜索答案,则为 sCU)并对 props 进行浅层比较(它会遍历 props 对象中的每个键并检查值是否为与 '===' 相同)。这在实践中意味着您的组件被认为是Pure。 现在只有当它的道具改变时才会改变!这是这里的关键信息。 第二个关键信息,React 路由器使用上下文将位置、匹配和历史对象从路由器传递到路由组件。 不使用道具。
现在让我们看看在实践中会发生什么,因为即使知道这一点,我还是觉得这很棘手:
案例1:连接后你的 props 有 3 个键:path、component 和 auth(由 connect 提供)。因此,实际上,您的包装组件根本不会在路由更改时重新渲染,因为它不在乎。当路由改变时,你的 props 不会改变,也不会更新。
案例3:现在连接后你的 props 有 4 个键:path、component、auth 和 anyprop。这里的技巧是 anyprop 是一个在每次调用组件时创建的对象。因此,每当调用您的组件时,都会进行此比较:a:1 === a:1,这(您可以尝试)给您错误,因此您的组件现在每次都会更新。但是请注意,您的组件仍然不关心路由,它的子组件关心。
案例2:现在这就是谜团了,因为我猜你在你的 App 文件中调用了这一行,并且那里不应该有对“auth”的引用,你应该有一个错误(至少这是我的猜测)。我的猜测是你的 App 文件中的“auth”引用了一个在那里定义的对象。
现在我们该怎么办?
我在这里看到两个选项:
告诉 React Redux 你的组件不是纯组件,这将移除 sCU 注入,你的组件现在将正确更新。
connect(mapStateToProps, null, null, 纯:假 )(私有路由)
使用 WithRouter(),这将导致将位置、匹配和历史对象作为道具注入组件。现在,我不知道内部结构,但我怀疑 React 路由器不会改变这些对象,所以每次路由改变时,它的 props 也会改变(sCU 返回 true),并且你的组件会正确更新。这样做的副作用是你的 React 树现在被很多 WithRouter 和 Route 东西污染了......
参考github问题:Dealing with Update Blocking
您可以在此处看到 withRouter 旨在作为快速修复而不是推荐的解决方案。没有提到使用 pure:false,所以我不知道这个修复会有多好。
我找到了第三种解决方案,但我不清楚它是否真的比 withRouter 更好,使用高阶组件。你将你的高阶组件连接到 Redux 存储,现在你的路由并不关心它呈现什么,HOC 处理它。
import Notlogged from "./Notlogged";
function Isloggedhoc( wrap: Component, islogged, ...props )
return islogged ? <Component ...props /> : <Notlogged ...props />;
const mapState = (state, ownprops) => (
islogged: state.logged,
...ownprops
);
export default connect(mapState)(Isloggedhoc);
在您的 App.js 中
<Route path="/PrivateRoute" render=props => <Isloadedhoc wrap=Mycomponent /> />
你甚至可以创建一个柯里化函数来缩短它:
function isLogged(component)
return function(props)
return <Isloadedhoc wrap=component ...props />;
;
这样使用它:
<Route path="/PrivateRoute" render=isLogged(Mycomponent) />
【讨论】:
好吧,我以为这会在link 之后发生,但事实并非如此。我的答案旨在建立在他问题的具体细节之上,尽管它可以应用于原始问题。【参考方案6】:我也遇到过这个问题,这是我的解决方案。
您只需从
import React from 'react';
import Route, Redirect, withRouter from 'react-router-dom';
import connect from 'react-redux';
// isAuthenticated is passed as prop here
const PrivateRoute = (component: Component, isAuthenticated , ...rest) =>
return <Route
...rest
render=
props =>
return isAuthenticated ?
(
<Component ...props />
)
:
(
<Redirect
to=
pathname: "/login",
state: from: props.location
/>
)
/>
;
const mapStateToProps = state => (
// isAuthenticated value is get from here
isAuthenticated : state.auth.isAuthenticated
);
export default withRouter(connect(
mapStateToProps, null, null, pure: false
)(PrivateRoute));
【讨论】:
【参考方案7】:打字稿
如果您正在寻找 Typescript 的解决方案,那么我就是这样设计的,
const PrivateRoute = ( component: Component, ...rest : any) => (
<Route
...rest
render=props =>
localStorage.getItem("authToken") ? (
<Component ...props />
) : (
<Redirect
to=
pathname: "/login",
state: from: props.location
/>
)
/>
);
<Router>
<Switch>
<PrivateRoute exact path="/home" component=Home />
<Route path="/login" component=Login />
</Switch>
</Router>
以防万一你想创建一个类然后像这样,
class PrivateRoute extends Route
render()
if (localStorage.getItem("authToken"))
return <Route ...this.props />
else
return <Redirect
to=
pathname: "/login",
state: from: this.props.location
/>
【讨论】:
【参考方案8】:根据react-router documentation,您可以只用withRouter
包装您的connect
函数:
// before
export default connect(mapStateToProps)(Something)
// after
import withRouter from 'react-router-dom'
export default withRouter(connect(mapStateToProps)(Something))
这对我有用,在这种情况下,我的视图开始随着路线更新。
【讨论】:
【参考方案9】:我也有类似@Rein 的问题。在我的例子中,PrivateRoute 看起来与原始版本几乎相同,但仅连接到 Redux 并使用它而不是原始示例中的 fakeAuth。
const PrivateRoute = ( component: Component, auth, ...rest ) => (
<Route
...rest
render=props =>
auth.isAuthenticated
? <Component ...props />
: <Redirect to= pathname: "/login" />
/>
);
PrivateRoute.propTypes =
auth: PropTypes.object.isRequired,
component: PropTypes.func.isRequired
const mapStateToProps = (state, ownProps) =>
return
auth: state.auth
;
export default connect(mapStateToProps)(PrivateRoute);
用法和结果:-
-
不工作但期望工作
<PrivateRoute path="/member" component=MemberPage />
工作但不希望这样使用
<PrivateRoute path="/member" component=MemberPage auth=auth />
工作。只是工作,但根本不想使用。从这点理解是,如果你将原始的 PrivateRoute 连接到 Redux,你需要传递一些额外的 props(任何 prop)来使 PrivateRoute 工作,否则它不起作用。 任何人,请就这种行为给出一些提示。这是我主要关心的问题。 As a New Question at
<PrivateRoute path="/member" component=MemberPage anyprop=a:1 />
【讨论】:
以上是关于反应路由器私有路由/重定向不起作用的主要内容,如果未能解决你的问题,请参考以下文章