Open hwen opened 6 years ago
原文发于我的博客:https://github.com/hwen/blogsome/issues/5
最近从vue转到react,遇到了一个问题。
vue
react
场景:
如图,有一个组件A,它接受一个从父级页面传过来的 dataList。而父级页面X里的 dataList 是 redux store 的数据。dataList 的数据会通过一个接口获取返回,也就是 dataList 是异步获取的。
组件A
页面X
组件A需要根据 dataList 来动态展示数据,用户与组件A交互后,变化的数据,希望回传给页面X。
大家可以看看 ElementUI 和 Ant Design React 的源码,可以发现子组件跟父组件的数据交互,都是通过回调事件(vue 里是 $emit),将更改后的数据返回给父组件。例如,antd 的 timepicker 组件,通过组件传进来的 onChange 事件,获取到所选择的时间。
ElementUI
Ant Design React
antd
timepicker
onChange
这样我们所封装的组件就是纯净的,没跟父组件耦合,可以用在任何地方。
考虑到上面的场景,用 antd 的方式来实现,就是图中的方式的方案1。
方案1
1.页面X通过props把dataList和回调事件传到onChange传到组件A,这里页面X的dataList,是 connect 到 redux store 的。 2.组件A通过 componentWillReceiveProps(17+ 用getDerivedStateFromProps)将 props dataList 同步到自己的 state 里。因为组件A不能直接更改props。 3.组件A交互后,更新自己的 state,更新展示,然后调用传进来的回调事件,并把更改后的回调数据作为参数 4.页面X获得更改后的数据
componentWillReceiveProps
getDerivedStateFromProps
目前为止看起来还行。不过,假设页面X里面,还有一个组件B,组件B也需要dataList作为展示,所以当dataList的数据发生改变时,它的UI也要同步改变。
组件B
为了要将组件A产生的更改也反馈到其他组件,所以我们就需要将组件A回调回来的数据,通过 action 来更新 redux store 了。那么,
5.页面X获得数据后,通过某个 action 更新了 redux store 的数据,这样组件B也得到了更新
问题在于,页面X更新了 redux store 后,更新会重复反馈到了组件A,组件A又经过componentWillReceiveProps获取到了新的props。但其实组件A现在的state已经是新的了,所以事实上并不会真正更改。
那么,既然这样,我还不如直接将组件A,和组件B 直接 connect 到 redux store,这样页面X压根就不用传什么数据到组件A,组件B了。组件A直接暗箱操作即可。
回头来说,如果我们直接把各种组件都直接 connenct 到 redux store,整件事的确就变得简单很多了。但这样组件A,组件B,组件xxx就变得不再纯洁了,它们依赖于应用的顶层状态(redux store),脱离了这个应用,它们是无法使用的。
connenct
redux store
而反过来看 ElementUI 和 Ant Design React 的组件不会跟任何应用状态有耦合,才使得它的组件可以被用到各个系统。
我们再来看看 redux 的思想,
在 Redux Store 中管理关系数据或嵌套数据的推荐做法是将这一部分视为数据库,并且将数据按范式化存储。
也就是说,redux是建议我们将组件直接connect到redux store的。因为是数据库嘛,所以你需要什么数据就查什么数据,你需要做更改就改数据库的。
事实上这种做法是非常有好处的。像上面“产生的问题”部分提到的问题就可以迎刃而解。而且,对于大型应用,有很多组件嵌套组件,里面再嵌套,再嵌套,这种多重嵌套的情况。像这种情况,如果你不用redux,难道要一层一层把更改的数据一层层回调上去??
想想就可怕好吧。这也就是为什么redux这种状态管理库出现的必要。
那么,所有组件都connect到redux,这么,组件复用性就相对很差了。
所以这两者之间需要权衡利弊。
如果是底层的组件,抽象层度较高的组件,应该用类似 antd 方式来实现这个组件。那么什么情况下,需要封装这类组件?
判断的基点在于这个组件是否跟业务的关联性,如果跟业务没什么关联,而且复用场景多,就很合适用 antd 的方式来封装了。
除此之外,其他应该都 connect 到 redux,统一来管理状态。
原文发于我的博客:https://github.com/hwen/blogsome/issues/5
最近从
vue
转到react
,遇到了一个问题。场景:
如图,有一个
组件A
,它接受一个从父级页面传过来的 dataList。而父级页面X
里的 dataList 是 redux store 的数据。dataList 的数据会通过一个接口获取返回,也就是 dataList 是异步获取的。组件A
需要根据 dataList 来动态展示数据,用户与组件A
交互后,变化的数据,希望回传给页面X
。保证组件的纯净
大家可以看看
ElementUI
和Ant Design React
的源码,可以发现子组件跟父组件的数据交互,都是通过回调事件(vue 里是 $emit),将更改后的数据返回给父组件。例如,antd
的timepicker
组件,通过组件传进来的onChange
事件,获取到所选择的时间。这样我们所封装的组件就是纯净的,没跟父组件耦合,可以用在任何地方。
考虑到上面的场景,用
antd
的方式来实现,就是图中的方式的方案1
。1.页面X通过props把dataList和回调事件传到onChange传到
组件A
,这里页面X的dataList,是 connect 到 redux store 的。 2.组件A
通过componentWillReceiveProps
(17+ 用getDerivedStateFromProps
)将 props dataList 同步到自己的 state 里。因为组件A
不能直接更改props。 3.组件A
交互后,更新自己的 state,更新展示,然后调用传进来的回调事件,并把更改后的回调数据作为参数 4.页面X获得更改后的数据产生的问题
目前为止看起来还行。不过,假设页面X里面,还有一个
组件B
,组件B
也需要dataList作为展示,所以当dataList的数据发生改变时,它的UI也要同步改变。为了要将
组件A
产生的更改也反馈到其他组件,所以我们就需要将组件A
回调回来的数据,通过 action 来更新 redux store 了。那么,5.页面X获得数据后,通过某个 action 更新了 redux store 的数据,这样
组件B
也得到了更新问题在于,页面X更新了 redux store 后,更新会重复反馈到了
组件A
,组件A
又经过componentWillReceiveProps
获取到了新的props。但其实组件A
现在的state已经是新的了,所以事实上并不会真正更改。那么,既然这样,我还不如直接将
组件A
,和组件B
直接 connect 到 redux store,这样页面X压根就不用传什么数据到组件A
,组件B
了。组件A
直接暗箱操作即可。不再纯洁的
组件A
回头来说,如果我们直接把各种组件都直接
connenct
到redux store
,整件事的确就变得简单很多了。但这样组件A
,组件B
,组件xxx就变得不再纯洁了,它们依赖于应用的顶层状态(redux store),脱离了这个应用,它们是无法使用的。而反过来看
ElementUI
和Ant Design React
的组件不会跟任何应用状态有耦合,才使得它的组件可以被用到各个系统。似乎走到死胡同?
我们再来看看 redux 的思想,
也就是说,redux是建议我们将组件直接connect到redux store的。因为是数据库嘛,所以你需要什么数据就查什么数据,你需要做更改就改数据库的。
事实上这种做法是非常有好处的。像上面“产生的问题”部分提到的问题就可以迎刃而解。而且,对于大型应用,有很多组件嵌套组件,里面再嵌套,再嵌套,这种多重嵌套的情况。像这种情况,如果你不用redux,难道要一层一层把更改的数据一层层回调上去??
想想就可怕好吧。这也就是为什么redux这种状态管理库出现的必要。
那么,所有组件都connect到redux,这么,组件复用性就相对很差了。
所以这两者之间需要权衡利弊。
结论是
如果是底层的组件,抽象层度较高的组件,应该用类似 antd 方式来实现这个组件。那么什么情况下,需要封装这类组件?
判断的基点在于这个组件是否跟业务的关联性,如果跟业务没什么关联,而且复用场景多,就很合适用 antd 的方式来封装了。
除此之外,其他应该都 connect 到 redux,统一来管理状态。