


An in-depth analysis of 10 ways to communicate between React components
[Related tutorial recommendations: React video tutorial]
I was temporarily transferred to another project team to work on a small project in the past two days. Iterate. The front end of this project uses React. It is just a small project, so it does not use state management libraries such as Redux. I just encountered a small problem: how to communicate between two unrelated components. I think this question is quite interesting, so I wrote down my thinking process so that everyone can discuss it together.
Although the focus is on the communication between two unrelated components, I will start with the most common communication between parent and child components, so that everyone can learn from the past. List the complete summary first, and then expand into details.
Summary of communication methods between components
- ##Parent component=> Child component:
- Props
- Instance Methods
##Child component=> Parent component: -
Callback Functions
- Event Bubbling
Between sibling components: -
Parent Component
-
Context
- Portals
- Global Variables
- Observer Pattern
- Redux etc
1. Props
This is the most common way to transfer information between react components. The parent component passes the data to the child component through props, and the child component passes
this.props Use the corresponding data<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>const Child = ({ name }) => {
<div>{name}</div>
}
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {
name: &#39;zach&#39;
}
}
render() {
return (
<Child name={this.state.name} />
)
}
}</pre><div class="contentsignin">Copy after login</div></div>
2. Instance Methods
The second way of passing information from a parent component to a child component may be unfamiliar to some students, but this method is very useful, so be sure to master it. . The principle is: the parent component can directly call the method of the child component instance by using
refs, see the following example: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>class Child extends React.Component {
myFunc() {
return "hello"
}
}
class Parent extends React.Component {
componentDidMount() {
var x = this.foo.myFunc() // x is now &#39;hello&#39;
}
render() {
return (
<Child
ref={foo => {
this.foo = foo
}}
/>
)
}
}</pre><div class="contentsignin">Copy after login</div></div>
The general process:
- The parent component passes a ref attribute to the child component in the form of callback-refs
- . This callback function receives react component instance/native DOM element as its parameter. When the parent component is mounted, react will execute the ref callback function and pass the child component instance as a parameter to the callback function, and then we assign the child component instance to this.foo.
Finally, we can use
this.foo - in the parent component to call the method of the subcomponent
class Modal extends React.Component { show = () => {// do something to show the modal} hide = () => {// do something to hide the modal} render() { return <div>I'm a modal</div> } } class Parent extends React.Component { componentDidMount() { if(// some condition) { this.modal.show() } } render() { return ( <Modal ref={el => { this.modal = el }} /> ) } }
3. Callback Functions
After talking about the two ways for parent components to transmit information to child components, let’s talk about the methods for child components to transmit information to parent components. The callback function method is also the most common way of react. The child component passes the data to the parent component by calling the callback function passed by the parent component.
const Child = ({ onClick }) => { <div onClick={() => onClick('zach')}>Click Me</div> } class Parent extends React.Component { handleClick = (data) => { console.log("Parent received value from child: " + data) } render() { return ( <Child onClick={this.handleClick} /> ) } }
4. Event Bubbling
This method actually has nothing to do with react itself. We use the event bubbling mechanism of the native dom element.
class Parent extends React.Component { render() { return ( <div onClick={this.handleClick}> <Child /> </div> ); } handleClick = () => { console.log('clicked') } } function Child { return ( <button>Click</button> ); }
With clever use of the event bubbling mechanism, we can easily receive click events from child component elements on the parent component element
5. Parent Component
After talking about the communication between parent and child components, let’s look at the communication methods between non-parent and child components. Generally speaking, if two non-parent-child components want to communicate, we can first see if they are sibling components, that is, whether they are under the same parent component. If not, consider whether it would be appropriate to wrap them in a component and thus become siblings. In this way, they can achieve data communication through the parent component as the middle layer.
class Parent extends React.Component { constructor(props) { super(props) this.state = {count: 0} } setCount = () => { this.setState({count: this.state.count + 1}) } render() { return ( <div> <SiblingA count={this.state.count} /> <SiblingB onClick={this.setCount} /> </div> ); } }
6. Context
Usually a front-end application will have some "global" data, such as the currently logged-in user information, UI theme, user-selected language, etc. These global data may be used by many components. When the component level is very deep, using our previous method, we have to pass it down layer by layer through props. This is obviously too troublesome. See the following example:
class App extends React.Component { render() { return <Toolbar theme="dark" />; } } function Toolbar(props) { return ( <div> <ThemedButton theme={props.theme} /> </div> ); } class ThemedButton extends React.Component { render() { return <Button theme={this.props.theme} />; } }
In the above example, in order to get the theme color for our Button element, we must pass the theme as props from App to Toolbar, then from Toolbar to ThemedButton, and finally Button finally gets it from the props of the parent component ThemedButton. theme theme. If we use Button in different components, we have to pass the theme everywhere like this example, which is extremely troublesome.
So react provides us with a new api: Context, we use Context to rewrite the above example
const ThemeContext = React.createContext('light'); class App extends React.Component { render() { return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ); } } function Toolbar() { return ( <div> <ThemedButton /> </div> ); } class ThemedButton extends React.Component { static contextType = ThemeContext; render() { return <Button theme={this.context} />; } }
A simple analysis:
React.createContext
创建了一个Context对象,假如某个组件订阅了这个对象,当react去渲染这个组件时,会从离这个组件最近的一个Provider
组件中读取当前的context值Context.Provider
: 每一个Context对象都有一个Provider
属性,这个属性是一个react组件。在Provider组件以内的所有组件都可以通过它订阅context值的变动。具体来说,Provider组件有一个叫value
的prop传递给所有内部组件,每当value
的值发生变化时,Provider内部的组件都会根据新value值重新渲染那内部的组件该怎么使用这个context对象里的东西呢?
a. 假如内部组件是用class声明的有状态组件:我们可以把Context对象赋值给这个类的属性contextType
,如上面所示的ThemedButton组件class ThemedButton extends React.Component { static contextType = ThemeContext; render() { const value = this.context return <Button theme={value} />; } }
Copy after loginb. 假如内部组件是用function创建的无状态组件:我们可以使用
Context.Consumer
,这也是Context对象直接提供给我们的组件,这个组件接受一个函数作为自己的child,这个函数的入参就是context的value,并返回一个react组件。可以将上面的ThemedButton改写下:function ThemedButton { return ( <ThemeContext.Consumer> {value => <Button theme={value} />} </ThemeContext.Consumer> ) }
Copy after login
最后提一句,context对于解决react组件层级很深的props传递很有效,但也不应该被滥用。只有像theme、language等这种全局属性(很多组件都有可能依赖它们)时,才考虑用context。如果只是单纯为了解决层级很深的props传递,可以直接用component composition
7. Portals
Portals也是react提供的新特性,虽然它并不是用来解决组件通信问题的,但因为它也涉及到了组件通信的问题,所以我也把它列在我们的十种方法里面。
Portals的主要应用场景是:当两个组件在react项目中是父子组件的关系,但在HTML DOM里并不想是父子元素的关系。
举个例子,有一个父组件Parent,它里面包含了一个子组件Tooltip,虽然在react层级上它们是父子关系,但我们希望子组件Tooltip渲染的元素在DOM中直接挂载在body节点里,而不是挂载在父组件的元素里。这样就可以避免父组件的一些样式(如overflow:hidden
、z-index
、position
等)导致子组件无法渲染成我们想要的样式。
如下图所示,父组件是这个红色框的范围,并且设置了overflow:hidden
,这时候我们的Tooltip元素超出了红色框的范围就被截断了。
怎么用portals解决呢?
首先,修改html文件,给portals增加一个节点
<html> <body> <div id="react-root"></div> <div id="portal-root"></div> </body> </html>
然后我们创建一个可复用的portal容器,这里使用了react hooks的语法,看不懂的先过去看下我另外一篇讲解react hooks的文章:30分钟精通React今年最劲爆的新特性——React Hooks
import { useEffect } from "react"; import { createPortal } from "react-dom"; const Portal = ({children}) => { const mount = document.getElementById("portal-root"); const el = document.createElement("div"); useEffect(() => { mount.appendChild(el); return () => mount.removeChild(el); }, [el, mount]); return createPortal(children, el) }; export default Portal;
最后在父组件中使用我们的portal容器组件,并将Tooltip作为children传给portal容器组件
const Parent = () => { const [coords, setCoords] = useState({}); return <div style={{overflow: "hidden"}}> <Button> Hover me </Button> <Portal> <Tooltip coords={coords}> Awesome content that is never cut off by its parent container! </Tooltip> </Portal> </div> }
这样就ok啦,虽然父组件仍然是overflow: hidden
,但我们的Tooltip再也不会被截断了,因为它直接超脱了,它渲染到body节点下的<div id="portal-root"></div>
里去了。
总结下适用的场景: Tooltip、Modal、Popup、Dropdown等等
8. Global Variables
哈哈,这也不失为一个可行的办法啊。当然你最好别用这种方法。
class ComponentA extends React.Component { handleClick = () => window.a = 'test' ... } class ComponentB extends React.Component { render() { return <div>{window.a}</div> } }
9. Observer Pattern
观察者模式是软件设计模式里很常见的一种,它提供了一个订阅模型,假如一个对象订阅了某个事件,当那个事件发生的时候,这个对象将收到通知。
这种模式对于我们前端开发者来说是最不陌生的了,因为我们经常会给某些元素添加绑定事件,会写很多的event handlers,比如给某个元素添加一个点击的响应事件elm.addEventListener('click', handleClickEvent)
,每当elm元素被点击时,这个点击事件会通知elm元素,然后我们的回调函数handleClickEvent会被执行。这个过程其实就是一个观察者模式的实现过程。
那这种模式跟我们讨论的react组件通信有什么关系呢?当我们有两个完全不相关的组件想要通信时,就可以利用这种模式,其中一个组件负责订阅某个消息,而另一个元素则负责发送这个消息。javascript提供了现成的api来发送自定义事件: CustomEvent
,我们可以直接利用起来。
首先,在ComponentA中,我们负责接受这个自定义事件:
class ComponentA extends React.Component { componentDidMount() { document.addEventListener('myEvent', this.handleEvent) } componentWillUnmount() { document.removeEventListener('myEvent', this.handleEvent) } handleEvent = (e) => { console.log(e.detail.log) //i'm zach } }
然后,ComponentB中,负责在合适的时候发送该自定义事件:
class ComponentB extends React.Component { sendEvent = () => { document.dispatchEvent(new CustomEvent('myEvent', { detail: { log: "i'm zach" } })) } render() { return <button onClick={this.sendEvent}>Send</button> } }
这样我们就用观察者模式实现了两个不相关组件之间的通信。当然现在的实现有个小问题,我们的事件都绑定在了document上,这样实现起来方便,但很容易导致一些冲突的出现,所以我们可以小小的改良下,独立一个小模块EventBus专门这件事:
class EventBus { constructor() { this.bus = document.createElement('fakeelement'); } addEventListener(event, callback) { this.bus.addEventListener(event, callback); } removeEventListener(event, callback) { this.bus.removeEventListener(event, callback); } dispatchEvent(event, detail = {}){ this.bus.dispatchEvent(new CustomEvent(event, { detail })); } } export default new EventBus
然后我们就可以愉快的使用它了,这样就避免了把所有事件都绑定在document上的问题:
import EventBus from './EventBus' class ComponentA extends React.Component { componentDidMount() { EventBus.addEventListener('myEvent', this.handleEvent) } componentWillUnmount() { EventBus.removeEventListener('myEvent', this.handleEvent) } handleEvent = (e) => { console.log(e.detail.log) //i'm zach } } class ComponentB extends React.Component { sendEvent = () => { EventBus.dispatchEvent('myEvent', {log: "i'm zach"})) } render() { return <button onClick={this.sendEvent}>Send</button> } }
最后我们也可以不依赖浏览器提供的api,手动实现一个观察者模式,或者叫pub/sub,或者就叫EventBus。
function EventBus() { const subscriptions = {}; this.subscribe = (eventType, callback) => { const id = Symbol('id'); if (!subscriptions[eventType]) subscriptions[eventType] = {}; subscriptions[eventType][id] = callback; return { unsubscribe: function unsubscribe() { delete subscriptions[eventType][id]; if (Object.getOwnPropertySymbols(subscriptions[eventType]).length === 0) { delete subscriptions[eventType]; } }, }; }; this.publish = (eventType, arg) => { if (!subscriptions[eventType]) return; Object.getOwnPropertySymbols(subscriptions[eventType]) .forEach(key => subscriptions[eventType][key](arg)); }; } export default EventBus;
10. Redux等
最后终于来到了大家喜闻乐见的Redux等状态管理库,当大家的项目比较大,前面讲的9种方法已经不能很好满足项目需求时,才考虑下使用redux这种状态管理库。这里就先不展开讲解redux了...否则我花这么大力气讲解前面9种方法的意义是什么???
总结
十种方法,每种方法都有对应的适合它的场景,大家在设计自己的组件前,一定要好好考虑清楚采用哪种方式来解决通信问题。
文初提到的那个小问题,最后我采用方案9,因为既然是小迭代项目,又是改别人的代码,当然最好避免对别人的代码进行太大幅度的改造。而pub/sub这种方式就挺小巧精致的,既不需要对别人的代码结构进行大改动,又可以满足产品需求。
更多编程相关知识,请访问:编程视频!!
The above is the detailed content of An in-depth analysis of 10 ways to communicate between React components. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics



React front-end and back-end separation guide: How to achieve front-end and back-end decoupling and independent deployment, specific code examples are required In today's web development environment, front-end and back-end separation has become a trend. By separating front-end and back-end code, development work can be made more flexible, efficient, and facilitate team collaboration. This article will introduce how to use React to achieve front-end and back-end separation, thereby achieving the goals of decoupling and independent deployment. First, we need to understand what front-end and back-end separation is. In the traditional web development model, the front-end and back-end are coupled

How to use React and Flask to build simple and easy-to-use web applications Introduction: With the development of the Internet, the needs of web applications are becoming more and more diverse and complex. In order to meet user requirements for ease of use and performance, it is becoming increasingly important to use modern technology stacks to build network applications. React and Flask are two very popular frameworks for front-end and back-end development, and they work well together to build simple and easy-to-use web applications. This article will detail how to leverage React and Flask

Many users always encounter some problems when playing some games on win10, such as screen freezes and blurred screens. At this time, we can solve the problem by turning on the directplay function, and the operation method of the function is also Very simple. How to install directplay, the old component of win10 1. Enter "Control Panel" in the search box and open it 2. Select large icons as the viewing method 3. Find "Programs and Features" 4. Click on the left to enable or turn off win functions 5. Select the old version here Just check the box

How to build a reliable messaging application with React and RabbitMQ Introduction: Modern applications need to support reliable messaging to achieve features such as real-time updates and data synchronization. React is a popular JavaScript library for building user interfaces, while RabbitMQ is a reliable messaging middleware. This article will introduce how to combine React and RabbitMQ to build a reliable messaging application, and provide specific code examples. RabbitMQ overview:

ReactRouter User Guide: How to Implement Front-End Routing Control With the popularity of single-page applications, front-end routing has become an important part that cannot be ignored. As the most popular routing library in the React ecosystem, ReactRouter provides rich functions and easy-to-use APIs, making the implementation of front-end routing very simple and flexible. This article will introduce how to use ReactRouter and provide some specific code examples. To install ReactRouter first, we need

How to use React and Apache Kafka to build real-time data processing applications Introduction: With the rise of big data and real-time data processing, building real-time data processing applications has become the pursuit of many developers. The combination of React, a popular front-end framework, and Apache Kafka, a high-performance distributed messaging system, can help us build real-time data processing applications. This article will introduce how to use React and Apache Kafka to build real-time data processing applications, and

PHP, Vue and React: How to choose the most suitable front-end framework? With the continuous development of Internet technology, front-end frameworks play a vital role in Web development. PHP, Vue and React are three representative front-end frameworks, each with its own unique characteristics and advantages. When choosing which front-end framework to use, developers need to make an informed decision based on project needs, team skills, and personal preferences. This article will compare the characteristics and uses of the three front-end frameworks PHP, Vue and React.

The default display behavior for components in the Angular framework is not for block-level elements. This design choice promotes encapsulation of component styles and encourages developers to consciously define how each component is displayed. By explicitly setting the CSS property display, the display of Angular components can be fully controlled to achieve the desired layout and responsiveness.
