React Router * Not Found *在主路由之前首先呈现

时间:2017-08-18 13:33:24

标签: reactjs react-router

我正在开发一个庞大的项目,我有一个授权系统,其中登录用户可以访问某些路线,而未找到组件,在登录时提供出用户。问题是,当用户被标记为登录并且具有有效令牌然后进入页面(例如/ account )时,组件未找到首先呈现,稍后呈现正确呈现组件帐户

我有一个异步 componentDidMount ,我在其中验证我的令牌,如果它有效,我设置 isLogged:true 的状态。同样在我的交换机的每个路由上,我正在检查 isLogged 的状态,然后我才发送相应的组件。

我的异步componentDidMount

{/* Username is from localstorage */}

async componentDidMount() {
  if ((USERNAME != "") && (USERNAME != null)) {
    const logged = await checkTheToken({'Username': USERNAME}).then((result) => {
      if(result.status == 202) {
         this.setState({isLogged: true});
         this.setState({checking: false});
      };
    });
  }
}

这是路由器(*假设我们正在尝试访问 / user 页面。请注意我使用react-router,react-router-dom ^ 4.1.1 * )

   if (this.state.checking) {
        return null;
   } else {
   return ( 
     <Router>
        <Switch>
            {/* This is Not Rendered First and Not Found rendered before FALSLY */}

            {this.state.isLogged && <Route exact path="/user" component={() => (<User isLogged={this.state.isLogged} username={USERNAME} />)} />}

            {/* This is Rendered First and Not Found is not rendering at all CORRECTLY */}

            <Route exact path="/test" component={() => (<Test isLogged={this.state.isLogged} />)} />

            <Route component={() => (<NotFound isLogged={this.state.isLogged} />)} />
        </Switch>
     </Router>
   );

我认为问题出在 {this.state.isLogged&amp;&amp; ...} 因为如果我们尝试访问 / test ,那么组件会在没有 Not Found 渲染的情况下正确呈现。

另外,我测试了所有生命周期方法

1 个答案:

答案 0 :(得分:1)

我认为你是对的,问题来自{this.state.isLogged && ...}

让我们一步一步来。

首先this.state.isLogged是假的。这确实意味着<User isLogged={this.state.isLogged} username={USERNAME} />当前不在ReactRouter配置中。

我们可以猜测ReactRouter将匹配默认组件(<Route component={() => (<NotFound isLogged={this.state.isLogged} />)} />),因为/user不在其配置中。

实际行为是正确的。

实现目标的最快方法是将令牌检查移动到您的子组件中,如下所示:

async componentDidMount() {
  if ((USERNAME != "") && (USERNAME != null)) {
    const logged = await checkTheToken({'Username': USERNAME}).then((result) => {
      if(result.status !== 202) {
        history.push('/not-found')
      } else {
        this.setState({isLogged: true});
        this.setState({checking: false});
      };
    });
  }
}

渲染功能是这样的:

render () {
 if(!this.state.isLogged) return <span />
 return <div>...</div>
}

这种方法的主要问题是它需要所有经过身份验证的组件才能实现这一点。

您还需要将代码分解为服务以避免多次调用。

第二种方法是将其伪装成代理组件,进行如下检查:

<Router>
    <Switch>
        <Route component={Authenticated}>
           <Route exact path="/user" component={() => (<User isLogged={this.state.isLogged} username={USERNAME} />)} />
        </Route>
        <Route exact path="/test" component={() => (<Test isLogged={this.state.isLogged} />)} />
        <Route component={() => (<NotFound isLogged={this.state.isLogged} />)} />
    </Switch>
 </Router>

此组件现在将是进行令牌检查的人。

渲染功能如下所示:

render () {
 if(!this.state.isLogged) return <span />
 return {this.props.children}
}

这里的主要问题是你不能轻易地在你的组件之间共享“this.state.isLogged”

如果您想使用某种全局状态共享和更新多个组件,我强烈建议您查看Redux

由于redux可以有一个艰难的学习曲线,如果你只需要分享这个单一的值,你可以使用可观察的模式尝试一些东西。 (示例:observable serviceobservable service + connector

我的所有代码示例都没有经过测试,而是为了引导您朝着正确的方向前进。在我看来,有很多方法可以实现你想要的东西,遗憾的是我只能告诉你为什么它目前无效,我希望我能给你足够的线索来找到适合你用例的解决方案。