【问题标题】: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')
);

【问题讨论】:

    标签: reactjs redux react-router react-redux


    【解决方案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”引用了一个在那里定义的对象。

      现在我们该怎么办?

      我在这里看到两个选项:

      1. 告诉 React Redux 你的组件不是纯组件,这将移除 sCU 注入,你的组件现在将正确更新。

        connect(mapStateToProps, null, null, { 纯:假 })(私有路由)

      2. 使用 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);
            

            用法和结果:-

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

            【讨论】:

              猜你喜欢
              • 2020-02-05
              • 2020-10-22
              • 2019-10-03
              • 2020-11-12
              • 2019-01-19
              • 2016-11-27
              • 2017-08-10
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多