// 子组件 class Son extends React.Component { constructor(props) { super(props); this.state = { checked: this.props.initialChecked } } onTextChange() { let newState = !this.state.checked; this.setState({ checked: newState }) // setState异步方法问题,注意传值 this.props.callbackParent(newState); } render() { let text = this.props.checked; let checked = this.state.checked; return { <label>{text}: <input type="checkbox" checked={checked} onChange={this.onTextChange.bind(this)} /></label> } } }
多想一点:
在本案例中,我们引用了三个 Son 子组件, 每个 Son 组件都独立工作互不干扰,该例中,增加了一个 totalChecked 来替代之前的 checked, 当组件触发onTextChange 后,触发父组件的回调函数使得父组件的值得以改变。
三、组件A和无关系组件B之间的通信
如果组件之间没有任何关系,或者组件嵌套的层次比较深,或者,你为了一些组件能够订阅,写入一些信号,不想让两个组件之间插入一个组件,而是让两个组件出于独立的关系。对于时间系统,有两个基本操作:
订阅: subscribe
监听: listen
并发送 send / 触发 trigger / 发布 publish / 发送 dispatch 通知那些想要的组件
1. Event Emitter/Target/Dispatcher
特点: 需要一个指定的订阅源
// to subscribe otherObiect.addEventListener('clickEvent', function() { alert('click!'); }) // to dispatch this.dispatchEvent('clickEvent');
2. Publish / Subscribe
特点: 触发的时候,不需要指定一个特定的源,使用全局对象广播的方式来处理事件
// to subscribe globalBroadcaster.subcribe('clickEvent', function() { alert('cilck!'); }) // to publish globalBroadcaster.publish('clickEvent');
这种方案还有一个插件可用, 即 PubSubJs;用法如下:
import Pubsub from 'pubsub-js'; ... // to subscribe Pubsub.subscribe('EVENT', (msg, param) => { console.log(msg, param); }); // to publish Pubsub.publish('EVENT', param);
3. Single
特点: 与 Event Emitter/Target/Dispatcher 类似,但是不要使用随机字符串作为事件触发的引用。触发事件的每一个对象都需要一个确切的名字,并且在触发的时候,也必须要指定确切的事件
// to subscribe otherObject.clicked.add(function() { alert('click'); }) // to dispatch this.clicked.dispatch();
React 团队使用的是:js-signals 它基于 Signals 模式,用起来相当不错。
事件订阅与取消
使用React事件的时候,必须关注以下两个方法:
componentDidMount
componentWillUnmount
在 componentDidMount 事件中,等待组件挂载 mounted 完成,再订阅事件;订阅的事件需要在组件卸载 componentWillUnmount 的时候取消事件的订阅。
因为组件的渲染和销毁是有 React 来控制的,我们不知道怎么引用他们,所以EventEmitter 模式在处理事件的时候用处不大,Pub/Sub 模式就好用些,因为我们不需要知道引用在哪。
ES6策略: yield and js-csp
ES6中有一种传递信息的方式,使用生成函数 generators 和 yield 关键字,用法参考以下例子
import csp from 'js-csp'; function* list() { for(var i = 0; i< arguments.length; i++) { yield arguments[i]; } return "done"; } var o = list(1, 2, 3); var cur = o.next; while (!cur.done) { cur = o.next(); console.log(cur); }
结束语
数据在组件中应该以什么样的方式进行传递取决于组件之间存在什么样的关系和当时的业务场景需求,大家应该根据项目合理选择数据处理的方案,这很可能减少你大量的代码量和代码逻辑。