TAT.Fujun react 组件间通信
In Web开发 on 2015年07月30日 by view: 15,027
24

通信是发送者通过某种媒体以某种格式来传递信息到收信者以达致某个目的(摘至维基百科)。

前两天为了练习 react,自己写了如下一个 Demo,功能很简单,展示学生成绩列表,支持按性别和姓名筛选。效果如下:

demo1

从上面的效果图,我们也可以看到如下的组件树结构:


structure

StudentScoreTable 有三个子组件:GenderFilter, NameFilter, ScoreTable。其中,ScoreTable 又包含若干个子组件 ScoreItem。从功能上,我们知道:

  1. 当选择性别后,要对 ScoreItem 做筛选
  2. 当输入姓名后,要对 ScoreItem 做筛选
  3. 当同时选择性别,输入姓名,两个筛选条件对 ScoreItem 生效
  4. 点击某 ScoreItem 的删除按钮后,删除此 ScoreItem

也就是说:

  1. 当 GenderFilter 发生改变(change)时,ScoreItem 要能感知到这个变化并做出相应反馈(进行筛选)
  2. 当 NameFilter 发生改变时,ScoreItem 也要能感知这个变化并做出相应反馈
  3. 当某 ScoreItem 删除后,可能 StudentScoreTable, GenderFilter, NameFilter 要做出相应反馈,即以后筛选,少了一个 ScoreItem

下面是我的第一版本实现

当然,功能都是实现了。但我却感觉非常不舒服。首先,让我们来看看数据在这些组件间是怎么通信的(数据怎么在这些组件间传递的)。StudentScoreTable 拥有数据模型,由于这些数据会变,所以使用 state 储存。为了让数据能够传到其子组件,使用了 props,如:

再往下看,就会知道,整个数据从 StudentScoreTable 到其包含的组件,都是使用 props 一层一层传下去的。当 ScoreItem 删除时,为了使用 StudentScoreTable 能够感知到,还利用 props 建立了一个事件传递链接:

ScoreItem 的 click 事件,调用其本身的 deleteHandler ->ScoreTable 的 onDelete –>ScoreTable 自身的 deleteItemHandler –> StudentScoreTable 的 deleteItem –> StudentScoreTable 的 onDeleteScoreItem,更新 state,完成 ScoreItem 的删除

从上面的数据,事件传递链,可以看出,都是一层一层传递,为了建立数据或事件的通信链,写了很多没用的 props 属性。这还不是问题的关键,毕竟这里只有三级组件嵌套,如果有 6 级,10 级呢?很显示,再这样一层一层地进行组件通信,是不可行的。

那么有没有一种方法,可以跨级调用组件的方法呢?比如可以直接通过 ScoreItem 直接调用 StudentScoreTable 的方法,让其更新其 state,这样就可以省掉中间的 ScoreTable 组件。在同事的提醒下,有了第二版实现:

这主要的变化就是要通信的两个组件,直接通过其组件句柄去直接访问其方法,没有了中间环节,代码也简洁了很多。

React 的官网上有这样一段话:

For communication between two components that don't have a parent-child relationship, you can set up your own global event system. Subscribe to events in componentDidMount(), unsubscribe in componentWillUnmount(), and call setState() when you receive an event.

大意就是说,可以使用观察者模式来解决组件间的通信。在这之上,学生成绩表的第三个版本出来了 (得先实现一个 PubSub,网上有很多,随便找一个):

在使用 PubSub 后,代码就更清晰明了,而且大家都知道,PubSub 有助于解藕,这非常有助于组织代码结构。

小结:

本文通过一个实际小例子讨论了 React 中组件通信的三种方法:

  1. 使用 props,构建通信链
  2. 在组件初始化的时候,保存组件的句柄,在其它组件中,使用此句柄达到直接访问组件的目的,完成通信
  3. 使用 PubSub 模式

其中,第 1 种方式,在组件嵌套较深时,显示不适用。第 2 种在组件很多时,也得定义维护很多变量。相比之下,PubSub 模式有助于解藕和代码组织,在 React 的组件通信时,推荐使用此方法。

最后,由于本人水平有限,也缺少 React 实战经验,有不严谨或不对的地方,还望大家指正。

附上源代码:http://share.weiyun.com/08ee52ccec9e21897d9f56ec0e62ec17

原创文章转载请注明:

转载自AlloyTeam:http://www.alloyteam.com/2015/07/react-zu-jian-jian-tong-xin/

  1. 不叮 2016 年 10 月 8 日

    观察者模式还是挺赞的,纠正一个错误:onGenderChange: function (gender) { this.setState({genderFilter: gender}); }, 这个函数是回调,函数体中 this 应该指向观察者才对,会报错的!

  2. 在线工具 2016 年 7 月 19 日

    确实使用第三种方式很不错,pubsub 的方式:https://github.com/hustcc/onfire.js 然后利用这个封装一个 mixin,爽爽的,另外做一些提示组件也不错。

  3. fancy 2016 年 7 月 6 日

    文件不存在啊

  4. ... 2016 年 5 月 16 日

    源代码文件不存在

  5. 行歌 2016 年 3 月 30 日

    onDeleteScoreItem 方法里的这行代码 this.setState(data, data); 应该写成 this.setState({data:data});

  6. 怡香 2015 年 12 月 2 日

    强烈支持,博主万岁!

  7. double 2015 年 11 月 30 日

    这些东西要组合起来用,深刻体会,消息不能滥用,否则就会织起一张复杂的消息网

  8. xcatliu 2015 年 11 月 18 日

    可以使用 redux

  9. erwin 2015 年 11 月 8 日

    大神 你这个屏幕录制 gif 怎么做到的?

  10. 神经衰弱的Jc 2015 年 10 月 27 日

    第二种模式 涨见识

  11. 玩笑脸王宁 2015 年 9 月 11 日

    其实 reflux 的本质也是实现了一个 PubSub 模式,Store 实现具体对 Action 的处理逻辑,需要使用该 Store 数据的组件,只要把它 mixin 进去,然后就能通过 state 进行访问了,在后面通过执行不同 action,对 Store 中保存的数据进行修改,之后就会应用到不同组件当中去。

  12. bird 2015 年 9 月 2 日

    外链过期了

  13. 最励志官网 2015 年 8 月 31 日

    不错,值得收藏分享!

  14. x 2015 年 8 月 21 日

    楼主的困惑,FLUX 可以解决。

  15. 晓风well 2015 年 8 月 10 日

    看着 URL 是拼音好不习惯

  16. 程序猿小卡 2015 年 8 月 8 日

    react 组件间的通信是个麻烦的事情,关键推荐实践是通过 flux,其实本质就是发布/订阅模式,但相对一般的实现,事件流转以及事件依赖的更为清晰。

    • TAT.Fujun

      TAT.Fujun 2015 年 8 月 10 日

      flux 还不是很熟哦

  17. 大米网 2015 年 8 月 3 日

    好教程

  18. 马铃薯 2015 年 8 月 3 日

    自定义一个 mixin,mixin 中初始化一个全局的 pubsub 对象,这么做应该可以吧

    • TAT.Fujun

      TAT.Fujun 2015 年 8 月 10 日

      接触 react 不久,mixin 把握还不够

  19. 飞翔的企鹅█━━◕ 2015 年 8 月 1 日

    不错,顶上!

  20. guest 2015 年 7 月 30 日

    哈哈哈

发表评论