2017-08-18 55 views
0

我是一个庞大的系统工程工作的主要路线之前,我有一个授权系统,其中已登录用户可以访问某些航线而未找到组件,在服务已注销用户。问题是,当用户被标记为登录,并具有有效的标记,然后进行到页面(例如/帐户),组分未找到呈现第一和第二以后的部件帐户呈现正确。阵营路由器*找不到*第一渲染

我有一个异步componentDidMount哪里验证我的令牌,如果它是有效的,我设置isLogged的状态:真正。同样在我的交换机中的每条路由上,我正在检查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}); 
     }; 
    }); 
    } 
} 

这是路由器(*假设我们正在试图访问/用户页面。请注意,我用的反应路由器,反应路由器-DOM^4.1 0.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 & & ...},因为如果我们尝试访问/test组件正确呈现没有未找到先渲染。

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

+0

您是否尝试过在'componentWillMount'中进行身份验证检查,您的组件在它进入'componentDidMount'之前已经进行了渲染。 – Purgatory

+0

我测试了所有的生命周期方法 –

+0

您可能需要添加一个状态字段,比如'checking“,默认为'true'。当你得到一个响应时,成功或错误会将'checking'设置为'false'。在你的render中if(this.state.checking)返回null;'。 – Purgatory

回答

1

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

让我们一步一步来。

第一个this.state.isLogged是虚假的。这意味着<User isLogged={this.state.isLogged} username={USERNAME} />目前不在ReactRouter配置中。

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

实际行为是正确的。

到archieve你的目标是将令牌检查进入你的子组件像这样的东西,最快的方式:

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> 
} 

的主要问题有了这个问题,它会要求所有你的认证组件来实现这一点。

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

一个第二个办法是fatorize成做检查像这样的代理组件这样的:

<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 service,observable service + connector

我所有的代码示例都未经过测试,并且在这里引导您朝着正确的方向发展。在我看来,有很多方法来实现你想要的东西,可悲的是我只能告诉你为什么它现在不起作用,我希望我给你足够的线索来找到适合你的用例的解决方案。

+0

谢谢你的回答!我在每个相应的组件上移动了checkToken。因此,ComponentDidMount上的每个组件都必须检查isLogged user。我不认为这是优化(可能不是),但它的工作原理。我更喜欢避免多次调用db当我加载我的组件,我更喜欢通过道具传递值从一个组件到另一个,但我认为是只有这样。 Regardin g第二种方法,我有反应路由器v4,我不能在我的路由器上添加嵌套路由。我试图降级路由器,但我收到多个错误让我知道,如果你想更新问题 –

+0

正如你所说我必须将代码因子分解成服务。我会检查它。 –

+1

我添加了一个服务共享值的示例https://jsfiddle.net/qcdmuzac/1/ – Okazari