【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>;}
}
进阶通讯方式(补充说明)
-
状态管理方案:Redux/MobX
-
事件总线:使用EventEmitter
-
Ref传递:forwardRef + useImperativeHandle
-
状态库:Recoil/Zustand
一、事件总线
事件总线是一种跨组件通信的方式,适用于任意组件之间的通信,尤其是非父子关系的组件。通过事件总线,组件可以订阅和触发事件,从而实现数据传递。
这里用了一个mitt,所以要下载一个依赖
npm i mitt --save
实现步骤:
-
创建一个全局的事件总线。
-
在需要接收数据的组件中订阅事件。
-
在需要发送数据的组件中触发事件。
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。通过它们,父组件可以访问子组件的特定方法或属性。
实现步骤:
-
使用
forwardRef包裹子组件,使其能够接收ref。 -
在子组件中使用
useImperativeHandle暴露特定的方法或属性。 -
在父组件中通过
ref调用子组件的方法。
import React, { useRef, useImperativeHandle, forwardRef } from 'react';interface ChildComponentRef {increment: () => voidgetCount: () => 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 或子组件逻辑的场景。
结语
希望本文的内容对你有用呦!
相关文章:
【React组件通讯双重视角】函数式 vs 类式开发指南
目录 前言 正文 父组件向子组件传值 函数式写法 类式写法 子组件向父组件传值 函数式写法 类式写法 兄弟组件通信 函数式写法 类式写法 跨层级通信(使用Context) 函数式写法 类式写法 进阶通讯方式(补充说明…...
VScode内接入deepseek包过程(本地部署版包会)
目录 1. 首先得有vscode软件 2. 在我们的电脑本地已经部署了ollama,我将以qwen作为实验例子 3. 在vscode上的扩展商店下载continue 4. 下载完成后,依次点击添加模型 5. 在这里可以添加,各种各样的模型,选择我们的ollama 6. 选…...
Ubuntu虚拟机NDK编译ffmpeg
目录 一、ffmpeg源码下载1、安装git(用于下载ffmpeg源码)2、创建源码目录,下载ffmpeg源码 二、下载ubuntu对应的NDK,并解压到opt下1、下载并解压2、配置 ~/.bashrc 三、源码编译、1、创建编译脚本2、脚本文件内容3、设置可执行权限并运行4、编译的结果在…...
机器学习:k近邻
所有代码和文档均在golitter/Decoding-ML-Top10: 使用 Python 优雅地实现机器学习十大经典算法。 (github.com),欢迎查看。 K 邻近算法(K-Nearest Neighbors,简称 KNN)是一种经典的机器学习算法,主要用于分类和回归任务…...
js第十二题
题十二:轮播图 要求: 1.鼠标不在图片上方时,进行自动轮播,并且左右箭头不会显示;当鼠标放在图片上方时,停止轮播,并且左右箭头会显示; 2.图片切换之后,图片中下方的小…...
讯飞唤醒+VOSK语音识别+DEEPSEEK大模型+讯飞离线合成实现纯离线大模型智能语音问答。
在信息爆炸的时代,智能语音问答系统正以前所未有的速度融入我们的日常生活。然而,随着数据泄露事件的频发,用户对于隐私保护的需求日益增强。想象一下,一个无需联网、即可响应你所有问题的智能助手——这就是纯离线大模型智能语音…...
Day4 25/2/17 MON
【一周刷爆LeetCode,算法大神左神(左程云)耗时100天打造算法与数据结构基础到高级全家桶教程,直击BTAJ等一线大厂必问算法面试题真题详解(马士兵)】https://www.bilibili.com/video/BV13g41157hK?p4&v…...
HTML【详解】input 标签
input 标签主要用于接收用户的输入,随 type 属性值的不同,变换其具体功能。 通用属性 属性属性值功能name字符串定义输入字段的名称,在表单提交时,服务器通过该名称来获取对应的值disabled布尔值禁用输入框,使其无法被…...
Jvascript网页设计案例:通过js实现一款密码强度检测,适用于等保测评整改
本文目录 前言功能预览样式特点总结:1. 整体视觉风格2. 密码输入框设计3. 强度指示条4. 结果文本与原因说明 功能特点总结:1. 密码强度检测2. 实时反馈机制3. 详细原因说明4. 视觉提示5. 交互体验优化 密码强度检测逻辑Html代码Javascript代码 前言 能满…...
LeetCode刷题---哈希表---290
单词规律 290. 单词规律 - 力扣(LeetCode) 题目: 给定一种规律 pattern 和一个字符串 s ,判断 s 是否遵循相同的规律。 这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 s 中的每个非空单词…...
用React实现一个登录界面
使用React来创建一个简单的登录表单。以下是一个基本的React登录界面示例: 1. 设置React项目 如果你还没有一个React项目,你可以使用Create React App来创建一个。按照之前的步骤安装Create React App,然后创建一个新项目。 2. 创建登录组…...
图论:tarjan 算法求解强连通分量
题目描述 有一个 n n n 个点, m m m 条边的有向图,请求出这个图点数大于 1 1 1 的强连通分量个数。 输入格式 第一行为两个整数 n n n 和 m m m。 第二行至 m 1 m1 m1 行,每一行有两个整数 a a a 和 b b b,表示有一条…...
Haskell语言的物联网
Haskell语言在物联网中的应用 引言 物联网(IoT,Internet of Things)是现代科技发展的重要领域,它将日常生活中的各种设备通过互联网连接起来,实现智能化的控制与管理。随着设备数量的激增,以及数据处理需…...
Java:单例模式(Singleton Pattern)及实现方式
一、单例模式的概念 单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点来访问该实例,是 Java 中最简单的设计模式之一。该模式常用于需要全局唯一实例的场景,例如日志记录器、配置管理、线程池、数据库…...
Python爬虫实战:股票分时数据抓取与存储 (1)
在金融数据分析中,股票分时数据是投资者和分析师的重要资源。它能够帮助我们了解股票在交易日内的价格波动情况,从而为交易决策提供依据。然而,获取这些数据往往需要借助专业的金融数据平台,其成本较高。幸运的是,通过…...
将图片base64编码后,数据转成图片
将图片数据进行base64编码后,可以在浏览器上查看图片,只需在前端加上data:image/png;base64,即可 在线工具: Base64转图片 - 加菲工具...
天翼云910B部署DeepSeek蒸馏70B LLaMA模型实践总结
一、项目背景与目标 本文记录在天翼云昇腾910B服务器上部署DeepSeek 70B模型的全过程。该模型是基于LLaMA架构的知识蒸馏版本,模型大小约132GB。 1.1 硬件环境 - 服务器配置:天翼云910B服务器 - NPU:8昇腾910B (每卡64GB显存) - 系统内存&…...
Budibase低代码平台体验
低代码平台还是很多的,体验了Nocobase,又开始体验Budibase, 其实Budibase和appsmith更相似一点。 Budibase的安装也很简单。 1.安装好操作系统Debian; 2.安装好docker, docker-compose 3.创建目录/data,在里面参考内容创建文件docker-compos…...
【R语言】GitHub Copilot安装-待解决
参考: 文章目录...
Playwright 自动化测试系统学习
入门 Playwright安装:Playwright入门之---安装-CSDN博客 生成测试:Playwright入门之---生成测试-CSDN博客 命令汇总:Playwright入门之---命令-CSDN博客...
Jetson Agx Orin平台preferred_stride调试记录--1924x720图像异常
1.问题描述 硬件: AGX Orin 在Jetpack 5.0.1和Jetpack 5.0.2上测试验证 图像分辨率在1920x720和1024x1920下图像采集正常 但是当采集图像分辨率为1924x720视频时,图像输出异常 像素格式:yuv_uyvy16 gstreamer命令如下 gst-launch-1.0 v4l2src device=/dev/video0 ! …...
DeepSeek冲击(含本地化部署实践)
DeepSeek无疑是春节档最火爆的话题,上线不足一月,其全球累计下载量已达4000万,反超ChatGPT成为全球增长最快的AI应用,并且完全开源。那么究竟DeepSeek有什么魔力,能够让大家趋之若鹜,他又将怎样改变世界AI格…...
CF 144A.Arrival of the General(Java实现)
题目分析 一个n个身高数据,问最高的到最前面,最矮的到最后面的最短交换次数 思路分析 首先,如果数据有重复项,例如示例二中,最矮的数据就是最后一个出现的数据位置,最高的数据就是最先出现的数据位置&…...
set的使用(c++)
STL里面已经为我们实现了两种红黑树,一种是存储关键字的set,另一种是存储双关键字的map,今天主要来了解set,无论是set还是map后面都跟一个multi,它们区别是set 不能存相同元素, multiset 可以存相同的元素&…...
未加cont修饰的左值引用不能绑定到右值
目录 一、问题背景 二、错误分析 三、警告分析 一、问题背景 在initial value of reference to non-const - C Forum看到如下有问题的代码,编译如下代码看看 #include <iostream> #include <cmath>int g(double x) { return std::floor(x); } int&a…...
5.日常英语笔记
sprouted tater 发芽的土豆 fluid 液体,流体 The doctor recommended drinking plenty of fluids 医生建议多喝流质 适应新环境 adapt to the new environment adjust to the new surroundings get used to the new setting accommodate oneself to the new circu…...
IDEA单元测试插件 SquareTest 延长试用期权限
SquareTest是一款强大的IDEA单元测试生成插件工具,具体使用方法就不过多介绍了,这里主要介绍变更试用期,方便大家使用 配置信息 我的电脑安装前提配置条件 IntelliJ IDEA 2023.2windows 系统 软件安装 IntelliJ IDEA 直接安装插件Squar…...
25/2/17 <嵌入式笔记> 桌宠代码解析
这个寒假跟着做了一个开源的桌宠,我们来解析下代码,加深理解。 代码中有开源作者的名字。可以去B站搜着跟着做。 首先看下main代码 #include "stm32f10x.h" // Device header #include "Delay.h" #include &quo…...
C/C++字符串格式化全解析:从printf到std::format的安全演进与实战指南
目录 C 语言中的格式化函数对比 1. printf / fprintf / sprintf 的异同 C 中的字符串格式化 1. 流式输出 (std::ostringstream) 2. C20/23 格式化库 (std::format,需编译器支持) 跨语言对比与最佳实践 实战建议 总结 C 语言中的格式化函数对比 1. printf / …...
油田安全系统:守护能源生命线的坚固壁垒
油田安全系统:不可或缺的能源护盾 在能源领域,油田作为国家重要的能源供应基地,其安全生产的重要性不言而喻。油田安全系统犹如一道坚固的护盾,全方位守护着人员生命、企业财产以及生态环境,是油田平稳运行与可持续发展…...
