当前位置: 首页 > news >正文

【React组件通讯双重视角】函数式 vs 类式开发指南

目录

前言

正文 

父组件向子组件传值

函数式写法 

类式写法 

子组件向父组件传值

函数式写法 

类式写法 

兄弟组件通信

函数式写法 

类式写法 

跨层级通信(使用Context)

函数式写法 

类式写法 

进阶通讯方式(补充说明)

一、事件总线:使用 EventEmitter

实现步骤:

二、Ref 传递:forwardRef + useImperativeHandle

实现步骤:

总结

事件总线(EventEmitter):

Ref 传递(forwardRef + useImperativeHandle):

结语 


前言

在现代前端开发中,React 作为最流行的 JavaScript 库之一,以其组件化、声明式编程和高性能的特点,成为构建用户界面的首选工具。然而,随着应用复杂度的提升,组件之间的通信问题逐渐成为开发者需要面对的核心挑战之一。无论是父子组件之间的数据传递,还是跨层级组件的状态共享,如何高效、优雅地实现组件间的通信,直接影响到代码的可维护性和应用的性能。

React 提供了多种组件通信的方式,以下是几种常见的模式:

  • Props 传递:父组件通过 props 向子组件传递数据。

  • 回调函数:子组件通过回调函数向父组件传递数据。

  • Context API:用于跨层级组件之间的数据共享。

  • 状态提升:将共享状态提升到共同的父组件中。

  • Ref 和事件机制:用于直接操作组件或触发事件。

接下来,我们将从 函数式组件 和 类式组件 两个角度,详细讲解这些通信方式的具体实现。

正文 

父组件向子组件传值

函数式写法 

// 父组件
function ParentComponent() {
  const [message] = useState('来自父组件的消息');

  return <ChildComponent message={message} />;
}

// 子组件
function ChildComponent({ message }) {
  return <div>{message}</div>;
}

类式写法 

// 父组件
class ParentComponent extends React.Component {
  state = { message: '来自父组件的消息' };

  render() {
    return <ChildComponent message={this.state.message} />;
  }
}

// 子组件
class ChildComponent extends React.Component {
  render() {
    return <div>{this.props.message}</div>;
  }
}

子组件向父组件传值

函数式写法 

// 父组件
function ParentComponent() {
  const handleChildData = (data) => {
    console.log('收到子组件数据:', data);
  };

  return <ChildComponent sendData={handleChildData} />;
}

// 子组件
function ChildComponent({ sendData }) {
  const sendMessage = () => {
    sendData('子组件发送的消息');
  };

  return <button onClick={sendMessage}>发送消息</button>;
}

类式写法 

// 父组件
class ParentComponent extends React.Component {
  handleChildData = (data) => {
    console.log('收到子组件数据:', data);
  };

  render() {
    return <ChildComponent sendData={this.handleChildData} />;
  }
}

// 子组件
class ChildComponent extends React.Component {
  sendMessage = () => {
    this.props.sendData('子组件发送的消息');
  };

  render() {
    return <button onClick={this.sendMessage}>发送消息</button>;
  }
}

兄弟组件通信

函数式写法 

function Parent() {
  const [sharedData, setSharedData] = useState('');

  return (
    <>
      <SiblingA setData={setSharedData} />
      <SiblingB data={sharedData} />
    </>
  );
}

function SiblingA({ setData }) {
  return <input onChange={(e) => setData(e.target.value)} />;
}

function SiblingB({ data }) {
  return <div>接收到的数据: {data}</div>;
}

类式写法 

class Parent extends React.Component {
  state = { sharedData: '' };

  setSharedData = (data) => {
    this.setState({ sharedData: data });
  };

  render() {
    return (
      <>
        <SiblingA setData={this.setSharedData} />
        <SiblingB data={this.state.sharedData} />
      </>
    );
  }
}

class SiblingA extends React.Component {
  handleChange = (e) => {
    this.props.setData(e.target.value);
  };

  render() {
    return <input onChange={this.handleChange} />;
  }
}

class SiblingB extends React.Component {
  render() {
    return <div>接收到的数据: {this.props.data}</div>;
  }
}

跨层级通信(使用Context)

函数式写法 

const MyContext = createContext();

function App() {
  return (
    <MyContext.Provider value="全局数据">
      <MiddleComponent />
    </MyContext.Provider>
  );
}

function MiddleComponent() {
  return <ChildComponent />;
}

function ChildComponent() {
  const value = useContext(MyContext);
  return <div>{value}</div>;
}

类式写法 

const MyContext = React.createContext();

class App extends React.Component {
  render() {
    return (
      <MyContext.Provider value="全局数据">
        <MiddleComponent />
      </MyContext.Provider>
    );
  }
}

class MiddleComponent extends React.Component {
  render() {
    return <ChildComponent />;
  }
}

class ChildComponent extends React.Component {
  static contextType = MyContext;
  
  render() {
    return <div>{this.context}</div>;
  }
}

进阶通讯方式(补充说明)

  1. 状态管理方案:Redux/MobX

  2. 事件总线:使用EventEmitter

  3. Ref传递:forwardRef + useImperativeHandle

  4. 状态库:Recoil/Zustand

一、事件总线

事件总线是一种跨组件通信的方式,适用于任意组件之间的通信,尤其是非父子关系的组件。通过事件总线,组件可以订阅和触发事件,从而实现数据传递。

这里用了一个mitt,所以要下载一个依赖

npm i mitt

实现步骤:
  1. 创建一个全局的事件总线。

  2. 在需要接收数据的组件中订阅事件。

  3. 在需要发送数据的组件中触发事件。

import React, { useEffect, useState } from 'react';
import mitt from 'mitt'

// 创建全局事件总线
const eventBus = mitt()

// 组件A:发送事件
function ComponentA() {
  const sendMessage = () => {
    eventBus.emit('message', 'Hello from ComponentA!')
  }

  return (
    <div>
      <button onClick={sendMessage}>发送消息</button>
    </div>
  )
}

// 组件B:接收事件
function ComponentB() {
  const [message, setMessage] = useState('')

  useEffect(() => {
    // 订阅事件
    function isString(value: any): value is string {
      return typeof value === 'string'
    }

    const handleMessage = (data: any) => {
      if (isString(data)) {
        setMessage(data)
      }
    }

    eventBus.on('message', handleMessage)

    // 清理订阅
    return () => {
      eventBus.off('message', handleMessage)
    }
  }, [])

  return (
    <div>
      <p>接收到的消息: {message}</p>
    </div>
  )
}


// 父组件
function App() {
  return (
    <div>
      <ComponentA />
      <ComponentB />
    </div>
  );
}

export default App;

二、Ref 传递:forwardRef + useImperativeHandle

forwardRef 和 useImperativeHandle 是 React 提供的用于操作子组件实例的 API。通过它们,父组件可以访问子组件的特定方法或属性。

实现步骤:
  1. 使用 forwardRef 包裹子组件,使其能够接收 ref

  2. 在子组件中使用 useImperativeHandle 暴露特定的方法或属性。

  3. 在父组件中通过 ref 调用子组件的方法。

import React, { useRef, useImperativeHandle, forwardRef } from 'react';

interface ChildComponentRef {
  increment: () => void
  getCount: () => number
}

// 子组件
const ChildComponent = forwardRef<ChildComponentRef>((props, ref) => {
  const [count, setCount] = useState(0)

  // 暴露方法给父组件
  useImperativeHandle(ref, () => ({
    increment: () => {
      setCount((prevCount) => prevCount + 1)
    },
    getCount: () => {
      return count
    },
  }))

  return (
    <div>
      <p>子组件计数: {count}</p>
    </div>
  )
})

// 父组件
function ParentComponent() {
  const childRef = useRef<ChildComponentRef>(null)

  const handleIncrement = () => {
    if (childRef.current) {
      childRef.current.increment() // 调用子组件的 increment 方法
    }
  }

  const handleGetCount = () => {
    if (childRef.current) {
      alert('当前计数: ' + childRef.current.getCount()) // 调用子组件的 getCount 方法
    }
  }

  return (
    <div>
      <ChildComponent ref={childRef} />
      <button onClick={handleIncrement}>增加计数</button>
      <button onClick={handleGetCount}>获取计数</button>
    </div>
  )
}
export default ParentComponent;

总结

事件总线(EventEmitter):
  • 适用于任意组件之间的通信。

  • 需要手动管理事件的订阅和清理。

  • 适合非父子关系的组件通信。

Ref 传递(forwardRef + useImperativeHandle):
  • 适用于父组件需要直接操作子组件方法或属性的场景。

  • 通过 useImperativeHandle 暴露特定的方法,保持组件的封装性。

  • 适合需要直接操作 DOM 或子组件逻辑的场景。 

结语 

希望本文的内容对你有用呦!

相关文章:

  • 第二章:基础概念精讲 - 第二节 - Tailwind CSS 颜色系统和主题定制
  • 什么是环形分区光源
  • Edge浏览器清理主页
  • 【漫话机器学习系列】092.模型的一致性(Consistency of a Model)
  • 4.SpringSecurity在分布式环境下的使用
  • ai智能电话机器人话术处理,呼叫系统部署语音话术设置
  • 《DeepSeek R1:7b 写一个python程序调用摄像头获取视频并显示》
  • 标贝科技参编国内首个AIGC大模型功能测试标准
  • 反向代理模块
  • 蓝桥杯(B组)-每日一题
  • DeepSeek R1 671b 满血版部署笔记
  • hbase快照同步到目标集群出现ERROR Multiple regions have the same startkey问题分析
  • Django学习笔记(第一天:Django基本知识简介与启动)
  • 仿 RabbitMQ 消息队列5(实战项目)
  • Pycharm中通过Anaconda虚拟环境创建项目
  • Lean 工具链教程 | Lake elan
  • 【Qt 常用控件】多元素控件(QListWidget、QTableWidgt、QTreeWidget)
  • PostgreSQL 开发利器:Navicat 核心功能与资源攻略
  • MFC程序设计(十一)单文档架构
  • w208基于spring boot物流管理系统设计与实现
  • 去年六成中小企业营收保持上升或持平,发展环境持续优化
  • 复旦一校友捐赠1亿元,却不留名
  • 价格周报|本周猪价继续下探,机构预计今年猪价中枢有支撑
  • 朱雀二号改进型遥二运载火箭发射成功
  • 技术派|威胁F-35、击落“死神”,胡塞武装防空战力如何?
  • 病愈出院、跳大神消灾也办酒,新华每日电讯:农村滥办酒席何时休