组件通信
一、认识
在 React
中,数据是自顶向下单向流动的,即从父组件到子组件。这条原则让组件之间的关系变得简单且可预测。
二、props 和 callback
props
和 callback
可以作为 React
组件最基本的通信方式,父组件可以通过 props
将信息传递给子组件,子组件可以通过执行 props
中的回调函数 callback
来触发父组件的方法,实现父与子的消息通讯。
-
父组件: 通过自身 state 改变,重新渲染,传递 props -> 通知子组件
-
子组件: 通过调用父组件 props 方法 -> 通知父组件。
2.1 单值通信
-
父组件
function App() {
const content = '内容';
return <Say content={content}></Say>;
} -
子组件
export default function Say(props) {
return (
<div>
<div>{props.content}</div>
</div>
);
}
2.2 多值通信
-
父组件
function App() {
const content = {
name: '柏拉图',
age: 23,
sex: '男',
};
return <Say {...content}></Say>;
}
export default App; -
子组件
export default function Say(props) {
return (
<div>
<div>{props.name}</div>
<div>{props.age}</div>
<div>{props.sex}</div>
</div>
);
}
三、ref
四、react-redux
五、context
5.1 管理状态
themeContext.js
用于集中管理theme
状态Context
import React from 'react';
// 创建 Context , 并提供默认值 'defaultValue'
export const ThemeContext = React.createContext('defaultValue');
// 提供 Context
export const ThemeProvider = ThemeContext.Provider;
// 提供 Context
export const ThemeConsumer = ThemeContext.Consumer;
5.2 发布状态
ContextPage.js
组件用于发布状态
import React, { useState } from 'react';
import { ThemeProvider } from './themeContext';
import ContextTypePage from './ContextTypePage';
import ContextTypePage1 from './ContextTypePage1';
import ConsumerPage from './ConsumerPage';
import ConsumerPage1 from './ConsumerPage1';
function ContextPage() {
const [theme, setTheme] = useState('color');
return <div>
<h3>ContextPage 页面</h3>
<ThemeProvider value={theme}>
{/* class 组件通过 static contxtType 接收 */}
<ContextTypePage />
{/* class 组件通过 组件.contextType 接收 */}
<ContextTypePage1 />
{/* 函数组件通过 Consumer 接收 */}
<ConsumerPage />
{/* class 组 件通过 Consumer 接收 */}
<ConsumerPage1 />
</ThemeProvider>
</div>
}
export default ContextPage;
5.3 订阅状态
-
订阅方式一:
ContextTypePage.js
class 组件通过static contextType = Context
来订阅状态import React from 'react';
import { ThemeContext } from './themeContext';
class ContextTypePage extends React.Component {
// class 组件必须使用 contextType 来接收
static contextType = ThemeContext;
constructor(props) {
super(props);
}
render() {
console.log(this);
const {context} = this;
return <div>
<h3>ContextTypePage 页面</h3>
{context && context}
</div>
}
}
export default ContextTypePage; -
订阅方式二:
ContextTypePage1.js
class 组件通过class类名.contextType = Context
来订阅状态import React from 'react';
import { ThemeContext } from './themeContext';
class ContextTypePage1 extends React.Component {
constructor(props) {
super(props);
}
render() {
console.log(this);
const { context } = this;
return <div>
<h3>ContextTypePage1 页面</h3>
{context && context}
</div>
}
}
// class 组件必须使用 contextType 来接收
ContextTypePage1.contextType = ThemeContext;
export default ContextTypePage1; -
订阅方式三:
ConsumerPage.js
函数组件通过Consumer
来订阅状态import { ThemeConsumer } from './themeContext';
function ConsumerPage() {
return <div>
<h3>ThemeConsumer 页面</h3>
<ThemeConsumer>
{
ctx => {
console.log(ctx);
return <div>{ctx}</div>
}
}
</ThemeConsumer>
</div>
}
export default ConsumerPage; -
订阅方式四:
ConsumerPage1.js
class 函数组件通过Consumer
来订阅状态import React from 'react';
import {ThemeConsumer} from './themeContext';
class ConsumerPage1 extends React.Component{
render(){
return <div>
<h3>ConsurmePage1 页面</h3>
<ThemeConsumer>
{
ctx =>{
console.log(ctx);
return <div>ctx</div>
}
}
</ThemeConsumer>
</div>
}
}
export default ConsumerPage1;
六、EventBus
利用EventBus
也可以实现组件通信,但是在React
中并不提倡用这种方式
5.1 通信过程
EventBus.js
class EventBus {
constructor() {
this.eventObj = {};
this.callbackId = 0;
}
$on(name, callback) {
if (!this.eventObj[name]) {
this.eventObj[name] = {};
}
const id = this.callbackId++;
this.eventObj[name][id] = callback;
return id;
}
$once(name, callback) {
if (!this.eventObj[name]) {
this.eventObj[name] = {};
}
const id = "D" + this.callbackId++;
this.eventObj[name][id] = callback;
return id;
}
$emit(name, ...args) {
const eventList = this.eventObj[name];
for (const id in eventList) {
eventList[id](...args);
if (id.indexOf("D") !== -1) {
delete eventList[id];
}
}
}
$off(name, id) {
delete this.eventObj[name][id];
if (!Object.keys(this.eventObj[name]).length) {
delete this.eventObj[name];
}
}
}
const eventBus = new EventBus();
export default eventBus
父组件
import React, { useState } from "react";
import ReactDOM from "react-dom";
import eventBus from "./eventBus";
import Child from "./child";
function App() {
const [num, setNum] = useState(2);
const handleSend = () => {
eventBus.$emit("father", num);
};
return (
<div>
<h1>父亲</h1>
<button onClick={handleSend}>传递</button>
<Child />
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
子组件
import React, { useEffect, useState } from "react";
import eventBus from "./eventBus";
function Child() {
const [num, setNum] = useState(1);
useEffect(() => {
eventBus.$on("father", (value) => {
setNum(value);
});
return ()=>{
eventBus.$off("father")
}
});
return (
<div>
{num}
<button onClick={handleChange}>改变</button>
</div>
);
}
export default Child;