React 是一种用于构建用户界面的 JavaScript 库,它注重组件化和单向数据流的思想,使得应用程序的整个 UI 都可以被声明式地描述。在 React 应用程序中,组件之间的通信是非常常见的情况。在这篇文章中,我们将介绍 React 组件之间通信的 5 种方式及实现方法,供大家学习和参考。
1. props 属性
props 是 React 组件之间通信的最基本方式。每个组件都可以接受传递给它的 props 属性,并将其用于渲染自身的 UI。父组件可以将任何类型的数据作为 props 传递给子组件,包括简单的字符串、数字、布尔值、对象甚至函数。
下面是一个示例代码:
// www.javascriptcn.com code example
// 父组件
import React from 'react';
import ChildComponent from './ChildComponent';
class ParentComponent extends React.Component {
render() {
return <ChildComponent name="John Doe" age={30} />;
}
}
// 子组件
import React from 'react';
class ChildComponent extends React.Component {
render() {
return (
<div>
<p>Name: {this.props.name}</p>
<p>Age: {this.props.age}</p>
</div>
);
}
}在上面的示例代码中,父组件 ParentComponent 传递了两个 props 属性 name 和 age 给子组件 ChildComponent,子组件通过 this.props 访问这两个属性并渲染在 UI 上。
2. 状态提升
状态提升是指将组件所需的状态上升到共同的祖先组件中,并通过 props 将状态传递给需要的子组件。这种方式通常用于解决多个组件之间需要共享状态的情况。
下面是一个示例代码:
// www.javascriptcn.com code example
// 祖先组件
import React from 'react';
import ChildrenComponent1 from './ChildrenComponent1';
import ChildrenComponent2 from './ChildrenComponent2';
class AncestorComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
this.handleIncrement = this.handleIncrement.bind(this);
}
handleIncrement() {
this.setState(prevState => ({
count: prevState.count + 1,
}));
}
render() {
return (
<div>
<ChildrenComponent1 count={this.state.count} onIncrement={this.handleIncrement} />
<ChildrenComponent2 count={this.state.count} />
</div>
);
}
}
// 子组件 1
import React from 'react';
class ChildrenComponent1 extends React.Component {
render() {
return (
<div>
<button onClick={this.props.onIncrement}>Increment</button>
</div>
);
}
}
// 子组件 2
import React from 'react';
class ChildrenComponent2 extends React.Component {
render() {
return <div>Count: {this.props.count}</div>;
}
}在上面的示例代码中, AncestorComponent 组件通过状态提升的方式将 count 状态传递给两个子组件 ChildrenComponent1 和 ChildrenComponent2,ChildrenComponent1 通过 onIncrement 回调函数更新 count 状态,ChildrenComponent2 访问 count 状态并渲染在 UI 上。
3. 上下文(Context)
上下文是 React 提供的一种在组件层次中共享数据的方式。通过上下文,组件可以访问到祖先组件中的数据,而不需要通过 props 属性一层层传递下去。
下面是一个示例代码:
// www.javascriptcn.com code example
// 祖先组件
import React from 'react';
import { MyContext } from './MyContext';
class AncestorComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'John Doe',
age: 30,
};
}
render() {
return (
<MyContext.Provider value={this.state}>
<ChildComponent />
</MyContext.Provider>
);
}
}
// 子组件
import React from 'react';
import { MyContext } from './MyContext';
class ChildComponent extends React.Component {
render() {
return (
<MyContext.Consumer>
{context => (
<div>
<p>Name: {context.name}</p>
<p>Age: {context.age}</p>
</div>
)}
</MyContext.Consumer>
);
}
}
// 上下文
import React from 'react';
export const MyContext = React.createContext();在上面的示例代码中, AncestorComponent 组件创建了一个 MyContext.Provider,并通过 value 属性将数据传递给子组件 ChildComponent。 ChildComponent 通过 MyContext.Consumer 组件订阅父组件的上下文,并在渲染时访问上下文中的数据。
4. 发布订阅(Pub/Sub)
发布订阅是一种松散耦合的通信方式,不同于父子组件之间直接的通信,Publish/Subscribe 模式下的两个组件并不知道彼此的存在,它们只有在某个中心对象(例如 Event Bus)上注册函数监听特定事件,当事件发生时中心对象通知所有订阅该事件的函数。
下面是一个示例代码:
// 中心对象(Event Bus) import EventEmitter from 'events'; const eventBus = new EventEmitter(); export default eventBus;
// www.javascriptcn.com code example
// 发布组件
import React from 'react';
import eventBus from './eventBus';
class PublishComponent extends React.Component {
handleClick() {
eventBus.emit('event', 'Hello!');
}
render() {
return <button onClick={this.handleClick}>Publish</button>;
}
}// www.javascriptcn.com code example
// 订阅组件
import React from 'react';
import eventBus from './eventBus';
class SubscribeComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
message: '',
};
this.handleEvent = this.handleEvent.bind(this);
}
componentDidMount() {
eventBus.on('event', this.handleEvent);
}
componentWillUnmount() {
eventBus.off('event', this.handleEvent);
}
handleEvent(message) {
this.setState({ message });
}
render() {
return <div>Message: {this.state.message}</div>;
}
}在上面的示例代码中, PublishComponent 组件通过点击按钮触发事件并发布消息,SubscribeComponent 组件监听该事件,并在事件发生时更新自己的状态。
5. 具名插槽(Slot)
具名插槽是 Vue 框架中的一种通信方式,React 中的实现可以使用 React.Children.map 和 React.cloneElement 方法。具名插槽让父组件可以向子组件中插入特定的内容,并根据需要传递一些数据给子组件。
下面是一个示例代码:
// www.javascriptcn.com code example
// 父组件
import React from 'react';
import ChildComponent from './ChildComponent';
class ParentComponent extends React.Component {
render() {
return (
<ChildComponent>
<div slotName="header">
<h1>Welcome</h1>
</div>
<div slotName="content">
<p>How are you?</p>
</div>
</ChildComponent>
);
}
}
// 子组件
import React from 'react';
class ChildComponent extends React.Component {
render() {
const slots = {};
React.Children.forEach(this.props.children, child => {
if (child.props.slotName) {
slots[child.props.slotName] = child;
}
});
return (
<div>
<div>{slots.header}</div>
<div>{slots.content}</div>
</div>
);
}
}在上面的示例代码中, ParentComponent 将两个带有 slotName 属性的组件作为 ChildComponent 的子组件传递给它。 ChildComponent 通过 React.Children.map 和 React.cloneElement 方法遍历子组件,将它们分别插入到自己的两个插槽中。
结语
以上就是 React 组件之间通信的 5 种方式及实现方法。每种方式都有其适用的场景,我们可以根据具体情况选择适合的方式来实现组件之间的通信。希望这篇文章对大家在学习 React 中有所帮助。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d63ca9a941bf7134beed38