反应路由器私有路由/重定向不起作用

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。但是,在登录并使用&lt;Redirect .../&gt; 或单击任何&lt;Link ...&gt; 到 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】:

你需要用&lt;Switch&gt;标签包裹你的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 传递给 @Danijel,你摇滚! :-)【参考方案4】:

我设法使用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);

【讨论】:

我必须这样做,并在路由中添加 &lt;Switch&gt; 以使其工作。【参考方案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】:

我也遇到过这个问题,这是我的解决方案。

您只需从 本身的状态中获取 isAuthenticated,而不是将 isAuthenticated 传递给每个 组件。

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);

用法和结果:-

    不工作但期望工作 &lt;PrivateRoute path="/member" component=MemberPage /&gt; 工作但不希望这样使用 &lt;PrivateRoute path="/member" component=MemberPage auth=auth /&gt; 工作。只是工作,但根本不想使用。从这点理解是,如果你将原始的 PrivateRoute 连接到 Redux,你需要传递一些额外的 props(任何 prop)来使 PrivateRoute 工作,否则它不起作用。 任何人,请就这种行为给出一些提示。这是我主要关心的问题。 As a New Question at &lt;PrivateRoute path="/member" component=MemberPage anyprop=a:1 /&gt;

【讨论】:

以上是关于反应路由器私有路由/重定向不起作用的主要内容,如果未能解决你的问题,请参考以下文章

反应路由器>重定向不起作用

反应路由器 v4 重定向不起作用

Angular ui 路由器 - 重定向根本不起作用

反应路由重定向 onClick

React 延迟加载的路由使得将不匹配的路由重定向到 /404 不起作用

Laravel 5 - 发布路由上的 htaccess HTTPS 重定向不起作用。