この記事の内容は、従来のコンポーネント間の通信と React コンポーネント間の通信の分析と比較 (コード例) です。必要な方は参考にしていただければ幸いです。手伝う。
React の最小の論理単位はコンポーネントです。コンポーネント間に結合関係がある場合、この記事では React におけるコンポーネント通信のさまざまな方法を紹介します。任意のコンポーネント間の通信は、親子コンポーネント、祖父と孫コンポーネント、兄弟コンポーネント、および任意のコンポーネントの 4 つのタイプのコンポーネント間通信に分類できます。最初の 3 つは任意のコンポーネントとしても数えられることに注意してください。
#親コンポーネントと子コンポーネントの間の通信は、親コンポーネントから子コンポーネントへの通信と、親コンポーネントから子コンポーネントへの通信の 2 つの状況に分けられます。子コンポーネントから親コンポーネントへ まず、コンポーネントはサブコンポーネントと通信します。 従来のアプローチは、初期化中のパラメーターの受け渡しと、インスタンス段階でのメソッドの呼び出しの 2 つの状況に分けられます。
class Child { constructor(name) { // 获取dom引用 this.$p = document.querySelector('#wp'); // 初始化时传入name this.updateName(name); } updateName(name) { // 对外提供更新的api this.name = name; // 更新dom this.$p.innerHTML = name; } } class Parent { constructor() { // 初始化阶段 this.child = new Child('yan'); setTimeout(() => { // 实例化阶段 this.child.updateName('hou'); }, 2000); } }
class Child extends Component { render() { return <p>{this.props.name}</p> } } class Parent extends Component { constructor() { // 初始化阶段 this.state = {name: 'yan'}; setTimeout(() => { // 实例化阶段 this.setState({name: 'hou'}) }, 2000); } render() { return <Child name={this.state.name} /> } }
まずコールバック関数を見てみましょう。たとえば、コールバック関数の利点は次のとおりです。欠点は、初期化中に渡す必要があり、取り消すことができないことと、渡せる関数が 1 つだけであることです。
class Child { constructor(cb) { // 调用父组件传入的回调函数,发送消息 setTimeout(() => { cb() }, 2000); } } class Parent { constructor() { // 初始化阶段,传入回调函数 this.child = new Child(function () { console.log('child update') }); } }
まず、メッセージ インターフェイス メソッドを見てみましょう。たとえば、単純な
EventEimtter
// 消息接口,订阅发布模式,类似绑定事件,触发事件 class EventEimtter { constructor() { this.eventMap = {}; } sub(name, cb) { const eventList = this.eventMap[name] = this.eventMap[name] || {}; eventList.push(cb); } pub(name, ...data) { (this.eventMap[name] || []).forEach(cb => cb(...data)); } } class Child extends EventEimtter { constructor() { super(); // 通过消息接口发布消息 setTimeout(() => { this.pub('update') }, 2000); } } class Parent { constructor() { // 初始化阶段,传入回调函数 this.child = new Child(); // 订阅子组件的消息 this.child.sub('update', function () { console.log('child update') }); } }
class Child extends Component { constructor(props) { setTimeout(() => { this.props.cb() }, 2000); } render() { return <p></p> } } class Parent extends Component { render() { return <Child cb={() => {console.log('update')}} /> } }
祖父と孫コンポーネント
の例を見てみましょう。 父と息子のコンポーネントは、実際には孫と孫のコンポーネントの特殊なケースと見なすことができます。ここでの孫と孫のコンポーネントは、祖父と孫を指すだけでなく、一般に先祖と子孫コンポーネント間の通信を分離することができます。親と子コンポーネント間の通信の問題は、階層ごとに属性を渡すというリダクション方法によって簡単に解決できます。は、複数の親子コンポーネント間の通信の問題に分解されます。レイヤーごとの転送の利点は、非常にシンプルであり、既存の知識で解決できることです。問題は、それが無駄になることです。コードが多く、非常に面倒です。ブリッジとして機能する中間のコンポーネントは、独自のプロパティに属さない多くのコンポーネントを導入します。React では、祖先コンポーネントはコンテキストを通じて子孫コンポーネントにプロパティを直接渡すことができます。これは、スタートレックのワームホールに似ています。コンテキストの特別なブリッジを介して、コンポーネントがメッセージを渡すために任意のレベルを越えることができます。通信する必要があるコンポーネント間でこのワームホールを開くにはどうすればよいでしょうか。双方向の宣言が必要です。つまり、祖先コンポーネントで属性を宣言し、子孫コンポーネントで属性を再度宣言し、その後、子孫コンポーネントで属性を読み取ることができます。 example
import PropTypes from 'prop-types'; class Child extends Component { // 后代组件声明需要读取context上的数据 static contextTypes = { text: PropTypes.string } render() { // 通过this.context 读取context上的数据 return <p>{this.context.text}</p> } } class Ancestor extends Component { // 祖先组件声明需要放入context上的数据 static childContextTypes = { text: PropTypes.string } // 祖先组件往context放入数据 getChildContext() { return {text: 'yanhaijing'} } }
ヒント: React Router のルーティングは、コンテキストを通じてルーティング属性を渡すことです
兄弟コンポーネント2 つのコンポーネントが兄弟である場合、親コンポーネントは 2 つのコンポーネント間で通信するためのブリッジとして使用されます。これは実際にはメイン モジュール モードです。次の例では、2 つのコンポーネントが通信します。サブコンポーネントは、親コンポーネントを通じてデジタル同期を表示する機能を実現します。
class Parent extends Component { constructor() { this.onChange = function (num) { this.setState({num}) }.bind(this); } render() { return ( <p> <Child1 num={this.state.num} onChange={this.onChange}> <Child2 num={this.state.num} onChange={this.onChange}> </p> ); } }
基于我们上面介绍的爷孙组件和兄弟组件,只要找到两个组件的共同祖先,就可以将任意组件之间的通信,转化为任意组件和共同祖先之间的通信,这个方法的好处就是非常简单,已知知识就能搞定,缺点就是上面两种模式缺点的叠加,除了临时方案,不建议使用这种方法
另一种比较常用的方法是消息中间件,就是引入一个全局消息工具,两个组件通过这个全局工具进行通信,这样两个组件间的通信,就通过全局消息媒介完成了
还记得上面介绍的消息基类吗?下面的例子中,组件1和组件2通过全局event进行通信
class EventEimtter { constructor() { this.eventMap = {}; } sub(name, cb) { const eventList = this.eventMap[name] = this.eventMap[name] || {}; eventList.push(cb); } pub(name, ...data) { (this.eventMap[name] || []).forEach(cb => cb(...data)); } } // 全局消息工具 const event = new EventEimtter; // 一个组件 class Element1 extends Component { constructor() { // 订阅消息 event.sub('element2update', () => {console.log('element2 update')}); } } // 另一个组件。 class Element2 extends Component { constructor() { // 发布消息 setTimeout(function () { event.pub('element2update') }, 2000) } }
消息中间件的模式非常简单,利用了观察者模式,将两个组件之间的耦合解耦成了组件和消息中心+消息名称的耦合,但为了解耦却引入全局消息中心和消息名称,消息中心对组件的侵入性很强,和第三方组件通信不能使用这种方式
小型项目比较适合使用这种方式,但随着项目规模的扩大,达到中等项目以后,消息名字爆炸式增长,消息名字的维护成了棘手的问题,重名概率极大,没有人敢随便删除消息信息,消息的发布者找不到消息订阅者的信息等
其实上面的问题也不是没有解决办法,重名的问题可以通过制定规范,消息命名空间等方式来极大降低冲突,其他问题可以通过把消息名字统一维护到一个文件,通过对消息的中心化管理,可以让很多问题都很容易解决
如果你的项目非常大,上面两种方案都不合适,那你可能需要一个状态管理工具,通过状态管理工具把组件之间的关系,和关系的处理逻辑从组建中抽象出来,并集中化到统一的地方来处理,Redux就是一个非常不错的状态管理工具
除了Redux,还有Mobx,Rematch,reselect等工具,本文不展开介绍,有机会后面单独成文,这些都是用来解决不同问题的,只要根据自己的场景选择合适的工具就好。
以上が従来のコンポーネント間の通信と React コンポーネント間の通信の分析と比較 (コード例)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。