【React系列二】React中组件通信的几种方式

3 阅读6分钟

【React系列一】初学 React:从 JSX 到组件运用

【React系列一】中我们对React 已经有了一个初步认识了,下面我们认识react组件通信的几种方式。

在构建复杂的React应用时,组件之间的通信是至关重要的。从简单的父子组件通信到跨组件状态同步,不同组件之间的通信方式多种多样。

1. 父子组件通信

父子组件通信是 React 中最基本的通信方式之一。在这种模式下,数据是从父组件通过 props 传递给子组件的,子组件接收到 props 后进行渲染或其他操作。

特点:

  • 单向数据流:数据从父组件流向子组件,子组件无法直接修改父组件传递过来的 props。

  • 简单明了:适用于父子组件之间的简单数据传递和交互。

  • 可维护性高:因为数据流清晰,易于追踪和调试。

父组件:

// 父组件

import React, { Component } from 'react'
import CChild from "./components/C-Child"

export default class CApp extends Component {
    state = {
        msg: '这是父组件的数据'
    }
    
    render() {
        return (
            <div>
                <h2>父组件</h2>

                <CChild msg={this.state.msg} />
            </div>
        )
    }
}

子组件:
// 子组件
import React, { Component } from 'react'

export default class CChild extends Component {
    render() {
        return (
            <div>
                <h4>子组件</h4>
                <p>{this.props.msg}</p>
            </div>
        )
    }
}
image.png

2. 子父组件通信

子父组件通信是指子组件向父组件传递数据或事件的过程。通常通过在子组件中定义回调函数,并将其作为 props 传递给子组件来实现。

特点:

  • 子组件向父组件传递数据或事件:子组件通过调用父组件传递的回调函数,向父组件传递数据或触发事件

  • 灵活性高:可以在需要的时候向父组件传递数据,实现灵活的交互。

  • PApp 组件定义了一个 callback 方法,这个方法用于接收子组件传递的数据。

父组件 PApp

在 render 方法中,PApp 渲染一个 PChild 子组件,并将 callback 方法作为 cb 属性传递给子组件。

//父组件PApp
import React, { Component } from 'react'
import PChild from './components/PChild'

export default class PApp extends Component {
    state = {
        msg: ''
    }

    callback = (newMsg) => {
        console.log('拿到子组件的数据: ' + newMsg);
        this.setState({
            msg: newMsg
        })
    }

    render() {
        return (
            <div>
                <h2>父组件 --- {this.state.msg}</h2>
                // 将回调函数传递给子组件 
                <PChild cb={this.callback} />
            </div>
        )
    }
}

子组件 PChild:

  • PChild 组件包含了一个状态 msg,代表子组件的数据。
  • PChild 组件有一个按钮,当按钮被点击时,触发 handler 方法。
  • handler 方法调用了父组件传递的回调函数 cb,并将子组件的状态数据 msg 作为参数传递给父组件。
 //子组件PChild
import React, { Component } from 'react'

export default class PChild extends Component {
    state = {
        msg: '来自子组件的数据'
    }
    
    // 处理按钮点击事件,调用父组件传递的回调函数
    handler = () => {
        this.props.cb(this.state.msg)// 将子组件的数据传递给父组件
    }

    render() {
        return (
            <div>
                <h4>子组件</h4>
                <button onClick={this.handler}>传递</button>
            </div>
        )
    }
}

使用示例

点击传递按钮前: image.png

点击传递按钮后:

image.png

3. 兄弟组件通信

兄弟组件通信是指不具有直接父子关系的两个组件之间进行数据传递和交互的过程。在 React 中,通常需要通过共享父组件来实现兄弟组件之间的通信。

注意:兄弟组件使用共同的父类作为桥梁,本质是父子之间通信。

  1. BApp 组件

    • BApp 组件是整个应用的父组件,它维护着一个状态 message,初始值为 'hello'

    • 在 render 方法中,BApp 返回了一个包含标题、BrotherA 和 BrotherB 组件的 JSX 结构。

    • 将 message 状态作为 BrotherB 组件的 props 传递给它。

import React, { Component } from 'react';
import BrotherA from "./components/BrotherA";
import BrotherB from "./components/BrotherB";

class BApp extends Component {
    state = {
        message: 'hello'
    }
    
    // 回调函数,用于更新 message 状态
    // 注意:React 中状态更新通常使用 setState 方法
    fn = (newMsg) => {
        console.log('父组件收到:' + newMsg);
        this.setState({
            message: newMsg
        })
    }
    
    render() {
        return (
            <div>
                <h1>父组件</h1>
                {/* 将 fn 方法作为 props 传递给 BrotherA 组件 */}
                <BrotherA cb={this.fn} />
                {/* 将 message 状态作为 props 传递给 BrotherB 组件 */}
                <BrotherB message={this.state.message} />
            </div>
        );
    }
}

export default BApp;
  1. BrotherA 组件
  • 定义了一个局部变量 msg,它的值是字符串 '来自子组件A的数据'。
  • 定义了一个函数 handle,用于处理点击事件。当组件标题被点击时,会调用 props 中传递的 cb 函数,并传递 msg 变量作为参数。
  • 返回一个包含标题的 JSX 结构,在标题上设置了点击事件处理函数为 handle
import React from 'react';

const BrotherA = props => {
    const msg = '来自子组件A的数据'

    const handle = () => {
        props.cb(msg)
    }
    return (
        <div>
            <h4 onClick={handle}>子组件A</h4>
        </div>
    );
};

export default BrotherA;
  1. BrotherB 组件

    • BrotherB 组件接收一个名为 message 的 prop,它来自于 BApp 的状态。
    • 在组件中显示了一个标题和 message 的值。
import React from 'react';

const BrotherB = props => {
    return (
        <div>
            <h4>子组件B -- {props.message}</h4>
        </div>
    );
};

export default BrotherB;

接下来我们验证一下:点击子组件A

image.png

点击之后如图结果:

image.png

4. 使用Context进行跨层级组件通信

当组件层级较深或通信的组件距离较远时,可以使用React的Context API进行跨层级通信。Context允许我们在组件树中传递数据,而不必手动通过Props一层层传递

创建:

  • 使用 React.createContext() 创建上下文对象
  • 并在组件中使用 Provider 提供数据,
  • 子组件通过 ConsumeruseContext 获取数据。

context.js

import React from 'react';

// 创建一个上下文对象
const { Provider, Consumer } = React.createContext();

// 导出 Provider 和 Consumer 组件,以便在其他地方使用
export {
    Provider,
    Consumer
}

BApp

  1. BApp 组件是一个类组件,它作为数据的提供者,使用 Provider 组件将数据传递给它的子组件。

  2. BApp 组件的 render 方法中,通过 Provider 组件的 value 属性传递了一个名为 message 的状态值

// BApp.jsx

import React, { Component } from 'react';
import BrotherB from "./components/BrotherB";

import { Provider } from "./context.js";

class BApp extends Component {
    state = {
        message: 'hello react', // 初始化状态值
    }

    render() {
        return (
            // 使用 Provider 组件提供数据
            <Provider value={this.state.message}>
                <div>
                    <h1>父组件</h1>
                    {/* 渲染子组件 */}
                    <BrotherB />
                </div>
            </Provider>
        );
    }
}

export default BApp;

BrotherB

  1. BrotherB 组件是一个函数组件,它作为数据的消费者,使用 Consumer 组件从上层组件(BApp)获取数据并进行渲染。

  2. BrotherB 组件中,通过 Consumer 组件的子组件函数来接收从 Provider 传递下来的值,并进行相应的渲染。

// BrotherB.jsx

import React from 'react';

import { Consumer } from '../provider.js';

const BrotherB = props => {

    return (
        // 使用 Consumer 组件消费数据
        <Consumer>
            {value => (
                <div>
                    {/* 使用从 Provider 传递下来的值进行渲染 */}
                    <h4>子组件B -- {value}</h4>
                </div>
            )}
        </Consumer>
    );
};
export default BrotherB;

补充

Consumer 组件内部,我们可以使用函数作为子组件

  1. 使用函数作为子组件:
<Consumer>
  {value => (
    // 在这里可以直接使用 value 进行渲染或处理
    <div>
      <h4>子组件B -- {value}</h4>
    </div>
  )}
</Consumer>

在这个示例中,<Consumer> 组件的子元素是一个函数,该函数接收 value 参数,这个 value 参数就是从 Provider 传递下来的值。在函数内部,可以直接使用 value 进行渲染或处理。

这种方式适用于在 JSX 内部直接定义渲染逻辑,通常更易读,因为它直接放在了 <Consumer> 标签内部。

效果图:

image.png

这样,就实现了 BApp 组件向 BrotherB 组件传递数据的功能。 Context API 的优点之一就是可以让组件之间直接传递数据,而无需通过 props 一层层地传递,从而简化了组件之间的关系。