跳到主要内容

组件通信

一、认识


React 中,数据是自顶向下单向流动的,即从父组件到子组件。这条原则让组件之间的关系变得简单且可预测。

二、props 和 callback


propscallback 可以作为 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;