Home >Web Front-end >JS Tutorial >Analysis and comparison of communication between traditional components and communication between React components (code example)
The content of this article is about the analysis and comparison of communication between traditional components and communication between React components (code examples). It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you. help.
The smallest logical unit in React is a component. If there is a coupling relationship between components, they will communicate. This article will introduce the different ways of component communication in React
By induction, Communication between any components can be classified into four types of inter-component communication, namely parent-child components, grandfather-grandson components, sibling components and any components.
It should be noted that the first three can also be counted as any components. category, so the last one is a universal method
The communication between parent and child components is divided into two situations: communication from parent component to child component and communication from child component to parent component. Let’s first introduce the parent component. Components communicate with sub-components.
The traditional approach is divided into two situations, namely parameter passing during initialization and method invocation in the instance phase. The example is as follows
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); } }
In React, the two situations are handled uniformly. All is done through properties. The reason why this is possible is that React will automatically re-render the sub-component when the properties are updated.
In the following example, the sub-component will automatically re-render after 2 seconds and obtain the new property value
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} /> } }
Let’s take a look at how sub-components communicate with parent components. There are two traditional methods, one is the callback function, and the other is to deploy a message interface for the sub-component.
Let’s first look at the callback function. For example, the advantage of the callback function is that it is very simple. The disadvantage is that it must be passed in during initialization and cannot be withdrawn, and only one function can be passed in.
class Child { constructor(cb) { // 调用父组件传入的回调函数,发送消息 setTimeout(() => { cb() }, 2000); } } class Parent { constructor() { // 初始化阶段,传入回调函数 this.child = new Child(function () { console.log('child update') }); } }
Let's take a look at the message interface method. First, we need a A base class that can publish and subscribe to messages. For example, a simple EventEimtter
is implemented below. In actual production, you can directly use class libraries written by others, such as @jsmini/event. Subcomponents inherit the message base class. With the ability to publish messages, the parent component can then subscribe to the message of the child component to realize the function of the child component communicating with the parent component.
The advantage of the message interface is that it can be subscribed anywhere and multiple times, and it can be subscribed multiple times. You can cancel the subscription, but the disadvantage is that it is a little troublesome. You need to introduce the message base class
// 消息接口,订阅发布模式,类似绑定事件,触发事件 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') }); } }
Backbone.js supports both callback functions and message interface methods, but React has chosen a relatively simple callback function mode. Let’s look at it below. Let’s look at an example of React
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')}} /> } }
Father and son components can actually be regarded as a special case of grandson and grandson components. The grandson and grandson components here not only refer to grandfather and grandson, but generally refer to ancestors and The communication between descendant components may be separated by many levels. We have solved the problem of parent-child component communication. According to the reduction method, it is easy to get the answer to the grandfather-grandchild component. That is to pass attributes layer by layer. The grandfather-grandchild component communication is decomposed into The problem of communication between multiple parent-child components
The advantage of layer-by-layer transfer is that it is very simple and can be solved with existing knowledge. The problem is that it will waste a lot of code and is very cumbersome. The components in the middle that serve as bridges will introduce many components that do not belong to Own properties
In React, ancestor components can pass properties directly to descendant components through context. It is a bit like the wormhole in Star Trek. Through the special bridge of context, you can cross any level to descendants. Components pass messages
How to open this wormhole between components that need to communicate? Two-way declaration is required, that is, declaring attributes on the ancestor component, declaring the attributes again on the descendant component, and then putting the attributes on the ancestor component. Then you can read the attributes on the descendant component. Here is an example
The advantage ofimport 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'} } }
context is that it can save the trouble of layer-by-layer transmission and control the visibility of data through bidirectional declaration. It is a solution when there are many layers; but the disadvantages are also obvious, just like global variables. , if not controlled, it can easily cause confusion, and it is also prone to the problem of duplicate name coverage.
My personal suggestion is that some read-only information shared by all components can be transferred using context, such as logged-in user information, etc.
Tips: React Router routing is to pass routing attributes through context
If the two components are brothers, you can The parent component is used as a bridge to communicate between the two components. This is actually the main module mode
In the following example, the two sub-components realize the function of displaying digital synchronization through the parent component
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> ); } }
The advantage of the main module pattern is decoupling. It decouples the coupling relationship between two sub-components into the coupling between the sub-component and the parent component. The benefits of collecting scattered things are very obvious and can bring better results. Maintainability and scalability
Any component includes the above three relationship components. The above three relationships should give priority to the methods introduced above. For any two components, There are three ways to communicate, namely common ancestor method, message middleware and state management
基于我们上面介绍的爷孙组件和兄弟组件,只要找到两个组件的共同祖先,就可以将任意组件之间的通信,转化为任意组件和共同祖先之间的通信,这个方法的好处就是非常简单,已知知识就能搞定,缺点就是上面两种模式缺点的叠加,除了临时方案,不建议使用这种方法
另一种比较常用的方法是消息中间件,就是引入一个全局消息工具,两个组件通过这个全局工具进行通信,这样两个组件间的通信,就通过全局消息媒介完成了
还记得上面介绍的消息基类吗?下面的例子中,组件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等工具,本文不展开介绍,有机会后面单独成文,这些都是用来解决不同问题的,只要根据自己的场景选择合适的工具就好。
The above is the detailed content of Analysis and comparison of communication between traditional components and communication between React components (code example). For more information, please follow other related articles on the PHP Chinese website!