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

【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 --save

实现步骤:
  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 或子组件逻辑的场景。 

结语 

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


http://www.kler.cn/a/551324.html

相关文章:

  • 安全测试|SQLMap渗透扫描工具
  • 基于Python的医院运营数据可视化平台:设计、实现与应用(上)
  • JS宏实例:数据透视工具的制作(二)
  • 拯救者电脑在重装系统之后电源计划丢失Fn+Q切换不了模式怎么恢复?
  • Linux centos8部署maven3.9.9
  • 基于大数据的汽车之家汽车数据分析系统的设计与实现
  • 网络安全概论——数字证书与公钥基础设施PKI
  • 游戏开发微信小程序--工具箱之父
  • 在Linux中Redis不支持lua脚本的处理方法
  • TraeAi上手体验
  • Spring Boot 如何实现自动配置?
  • 【MediaTek】 T750 openwrt-23.05编 cannot find dependency libexpat for libmesode
  • 过于依赖chatgpt编程会有哪些弊端?
  • 机器人路径规划
  • 低成本、高效率且成熟的电商实时数据采集方案:梦想成真?
  • 基于Dapr与Kubernetes的弹性事件驱动架构:构建跨云可观测的松散耦合系统
  • DeepSeek 助力 Vue 开发:打造丝滑的日期选择器(Date Picker),未使用第三方插件
  • Unity学习part2
  • 论文阅读2——S波段宽波束圆极化天线设计
  • 如何利用 Java 在线生成 PDF 文件