【问题标题】:React router private route not getting props from state (e.g. authentication state)反应路由器私有路由未从状态获取道具(例如身份验证状态)
【发布时间】:2019-11-05 04:55:01
【问题描述】:

我正在尝试实现一个私有路由组件来重定向未经过身份验证的用户。问题是当我渲染像<PrivateRoute authenticated={this.state.isAuthenticated} path='/private' component={Panel} currentUser={this.state.currentUser} 这样的组件时,私有路由会将经过身份验证的用户重定向到登录页面,而不是转到面板。

App.js 中,我渲染了所有路由,包括<PrivateRoute/>,并在ComponentDidMount() 中设置了currentUserisAuthenticated 状态变量,但我无法将它们传递给PrivateRoute

App.js

//imports...
class App extends Component {

    state = {
        currentUser: null,
        isAuthenticated: false,
        isLoading: false
    }

    loadCurrentUser = () => {
    this.setState({
      isLoading: true
        });
        // imported method
        getCurrentUser()
            .then(response => {
                this.setState({
                    currentUser: response,
                    isAuthenticated: true,
                    isLoading: false
                });
            })
            .catch(error => {
                console.log(error)
                this.setState({
                    isLoading: false
                });  
            });
  }

  componentDidMount() {
        this.loadCurrentUser();
  }

  handleLogin = () => {
        this.loadCurrentUser();
        this.props.history.push("/");
    }

    render () {
        return (
            <React.Fragment>
                <Navigation
                    currentUser={this.state.currentUser}
                    isAuthenticated={this.state.isAuthenticated}
                    handleLogout={this.handleLogout} />
                <Switch>
                    <PrivateRoute
                        authenticated={this.state.isAuthenticated}
                        exact
                        path='/postulante'
                        component={Panel}
                        currentUser={this.state.currentUser} />
                    <Route
                        exact
                        path='/'
                        render={
                            (props) => <Landing {...props} />
                        } />

                    <Route
                        path="/login"
                        exact
                        render={
                            (props) =>  <Login onLogin={this.handleLogin} {...props} />
                        } />
                </Switch>
            </React.Fragment>
        );
    }
}

export default withRouter(App);

请注意,&lt;Navigation /&gt; 组件确实获得了正确的状态变量。

PrivateRoute.js

//imports...
const PrivateRoute = ({ component: Component, authenticated, ...rest }) => (
    <Route
      {...rest}
      render={props =>
        authenticated ? (
          <Component {...rest} {...props} />
        ) : (
          <Redirect
            to={{
              pathname: '/login',
              state: { from: props.location }
            }}
          />
        )
      }
    />
);
export default PrivateRoute

【问题讨论】:

  • 嗨!刚刚给你写了一个解决方案和一个沙箱供你参考。如果您有任何问题,请告诉我:)

标签: reactjs routes react-router


【解决方案1】:

问题与 PrivateRoute 组件在第一次渲染时没有来自主 App 组件的任何更新道具有关。

如果您直接导航到PrivateRoute 路径而不先进入任何其他路由,您将被重定向回/login。您的 PrivateRoute 尝试在父 App 的 componentDidMount() 逻辑完成之前呈现。所以 isAuthenticated 被传递为假。

如果您从HomeLogin 开始,然后使用Link 转到PrivateRoute,则会发生相反的情况。

最终,这就是为什么人们使用像 redux 这样的状态管理工具来让经过身份验证的状态在全球范围内共享,而不是通过父组件传递。

虽然有解决方法!

参考沙盒:https://codesandbox.io/s/intelligent-dan-uskcy

  1. 我们可以通过使用额外的状态值来解决这个问题 App 组件是否曾被初始化。我们称之为 wasInitialized
  2. PrivateRoute 将接收它作为称为 wasInitialized 的道具,如果 我们直接进入它的组件路径,wasInitialized 将是 false,直到 App 有机会完成其 componentDidMount() 逻辑。
  3. 如果 wasInitialized 是假的,我们不会重定向到 /login, 相反,我们将只显示一个空字符串,给出父 App 的 componentDidMount() 是时候执行和更新 isAuthenticated 值。
  4. 现在让我们看一下这一行:

    <Route {...rest} render={props => auth === true ? <Component {...props} /> : !wasInitialized ? "" : <Redirect to="/login" /> }

    在下一次重新渲染中,isAuthenticated 将是 true 或 错误的。如果用户是Authenticated,我们渲染预期的组件。如果用户未通过身份验证,我们进行下一个检查。现在 wasInitialized 的值为 true,因此 check 的计算结果为 false。因此,由于两项检查都没有通过,我们重定向到/login

App.js

class App extends Component {

state = {
    currentUser: null,
    isAuthenticated: false,
    isLoading: false,
    wasInitialized: false
}

loadCurrentUser = () => {
this.setState({
  isLoading: true
    });
    // imported method
    getCurrentUser()
        .then(response => {
            this.setState({
                currentUser: response,
                isAuthenticated: true,
                wasInitialized: true,
                isLoading: false
            });
        })
        .catch(error => {
            console.log(error)
            this.setState({
                isLoading: false,
                wasInitialized: true
            });  
        });
  }

  componentDidMount() {
        this.loadCurrentUser();
  }

  handleLogin = () => {
        this.loadCurrentUser();
        this.props.history.push("/");
    }
render () {
    return (
        <React.Fragment>
            <Navigation
                currentUser={this.state.currentUser}
                isAuthenticated={this.state.isAuthenticated}
                handleLogout={this.handleLogout} />
            <Switch>
                <PrivateRoute
                    authenticated={this.state.isAuthenticated}
                    path='/postulante'
                    component={Panel}
                    wasInitialized={this.state.wasInitialized}
                    currentUser={this.state.currentUser} />
                <Route
                    exact
                    path='/'
                    render={
                        (props) => <Landing {...props} />
                    } />

                <Route
                    path="/login"
                    exact
                    render={
                        (props) =>  <Login onLogin={this.handleLogin} {...props} />
                    } />
            </Switch>
        </React.Fragment>
    );
}
}

export default withRouter(App);

私人

import React from "react";
import { Route, Redirect } from "react-router-dom";

const PrivateRoute = ({
  component: Component,
  auth,
  wasInitialized,
  ...rest
}) => {
  return (
    <Route
      {...rest}
      render={props =>
        auth === true ? (
          <Component {...props} />
        ) : !wasInitialized ? (
          ""
        ) : (
          <Redirect to="/login" />
        )
      }
    />
  );
};

export default PrivateRoute;

【讨论】:

  • 是的,我就是这么想的,但我不知道如何解决。谢谢!顺便说一句,在解决方案中,App.js 应该已经在状态下初始化了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-06-01
  • 2018-12-19
  • 2021-08-06
  • 1970-01-01
  • 2023-02-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多