我具体描述下问题产生的原因吧。
现在有三个组件(暂定a、b、c)去实现一个用户认证的功能。
a组件是父组件(相对b、c,a组件外面还有父组件),负责接收父组件传递下来用户的认证信息,b组件是用户用来填写信息的地方,c组件是表示用户的认证状态(未认证、审核、已认证、认证失败,分别对应状态为0,1,2,-1)。
假如用户认证失败了,c组件内部会有一个按钮,让用户重新认证(切换b、c组件)。
我一开始写的时候是这么想的:
提取一个状态到a组件中(暂定show),show的值是根据a组件传递下来的认证状态(props)来确定的,通过show来改变应该展示哪个组件。
当用户认证失败的时候点击c组件中的“重新认证”,通过回调函数改变父组件show的状态,完成b、c组件的切换。
问题就出现在这个show上(暂定true显示b组件,false显示c组件),show的默认值为true(让用户输入信息)。
我在a组件的componentWillReceiveProps里面去改变show的状态(didMount拿不到props,render里面不能操作state)。
现在不论用户是否通过认证了,a组件加载的时候默认展示的都是b组件,因为componentWillReceiveProps没有被调用。刷新页面之后才会展示c组件。
现在我能想到的解决办法就是让a组件去拿数据,然后确定show的状态。我想问下各位大神,还有更好的办法帮我实现这个功能吗?
补一张图吧,看的清楚点
a:
雷雷c:
雷雷应该有一个组件去决定是否要进行用户认证,如果你想让登录组件全部包揽这个逻辑,那你需要一个加载状态,刚开始的时候处于加载中,什么都不显示。所谓加载就是决定是否要认证(发API请求等等),加载完毕后就可以决定是否要认证。
我100%保证
componentDidMount()
是可以拿到props参数的,通过 this.props 获取,但是componentDidMount() 只在组件创建时调用一次,通常情况下如果要加载数据就在 componentDidMount() 里执行。show为什么要是a组件的state?你把show当作render函数中的一个局部变量就行了。
假定a组件从props里传递过来的认证状态变量命名为 auth,你的 a 组件render方法可以这样写:
========= 更新 =========
第一次没看到你的C组件居然还有一个回调箭头来改变A组件的show!
对,这一步也是错误的设计。
A组件得到的认证信息是A组件的父组件传递下来的,那么这个show就应该要唯一依赖于这一个信息的,C组件具有改变这个认证信息的功能,那么,A组件有义务把这个改变通知给A组件的父组件,而不是私自地悄么声地改变自己组件的state,即你在这里设定的show这个state,就草草了事。试想,这个时候,A组件得到的props是认证失败,渲染了C组件,C组件有重新认证的功能,用户重新认证成功,C组件又通知A组件认证成功了,这个时候A组件要相信谁?props和state就不同步了!更惨的是A组件的父组件,他还傻乎乎地以为自己拿到的是正确的信息,还通过props告诉A组件,用户认证失败啦,殊不知A组件已经串通他底下的小弟,把认证信息都给改了!倘若这个时候A组件有个兄弟叫A2组件,A2也通过props从他们共同的父组件接收认证信息,那就会出现A和底下一帮家伙悄悄重新认证了,而A的父亲和兄弟还蒙在鼓里,页面显示就不一致啦!
正确的设计是,A组件在接收到C组件重新认证成功的事件通知,需要把这个通知继续往上传递,告诉A组件的父组件,父组件接收到这个事件,改变他自己的状态,进而改变传递给A组件的props,A组件props改变,导致A组件重绘,从而replace C with B。
认证信息只能保存一份,你的例子里,认证信息放在A组件的父组件里,因此,要修改这个认证信息,也应该在A组件的父组件里完成。因此,这个show其实只是一个根据props产生的中间变量,根本无需设计成A的state。
如果你用redux就没有这个疑惑了。