网站建设有哪些/最新热点新闻事件
引言
以 Bootstrap 为例,使用模态框编写一个简单的消息框:
import { useState } from "react";
import { Modal } from "react-bootstrap";
import Button from "react-bootstrap/Button";
import 'bootstrap/dist/css/bootstrap.min.css';function App() {let [show, setShow] = useState(false);const handleConfirm = () => {setShow(false);console.log("confirm");};const handleCancel = () => {setShow(false);console.log("cancel");};return (<div><Button variant="primary" onClick={() => setShow(true)}>弹窗</Button><Modal show={show}><Modal.Header><Modal.Title>我是标题</Modal.Title></Modal.Header><Modal.Body>Hello World</Modal.Body><Modal.Footer><Button variant="primary" onClick={handleConfirm}>确定</Button><Button variant="secondary" onClick={handleCancel}>取消</Button></Modal.Footer></Modal></div>);
}export default App;
整段代码十分复杂。
Bootstrap 的模态框使用 show
属性决定是否显示,因此我们不得不创建一个 state 来保存是否展示模态框。然后还得自己手动在按钮的点击事件里控制模态框的展示。
如果你编写过传统桌面软件,弹一个消息框应该是很简单的事情,就像
if (MessageBox.show('我是标题', 'HelloWorld', MessageBox.YesNo) == MessageBox.Yes)console.log('确定');
elseconsole.log('取消');
一样。
那么下面我们就朝着这个方向,尝试将上面的 React 代码简化。
0. 简单封装
首先从 HTML 代码开始简化。先封装成一个简单的受控组件:
import React, { useMemo } from "react";
import { useState, createContext, useRef } from "react";
import { Button, Modal } from "react-bootstrap";/*** 类 Windows 消息框组件。* @param {object} props * @param {string} props.title 消息框标题* @param {string} props.message 消息框内容* @param {string} [props.type="ok"] 消息框类型* @param {boolean} [props.showModal=false] 是否显示消息框* @param {function} [props.onResult] 消息框结果回调* @returns {JSX.Element}*/
function MessageBox(props) {let title = props.title;let message = props.message;let type = props.type || 'ok';let showModal = props.showModal || false;let onResult = props.onResult || (() => {});let buttons = null;// 处理不同按钮const handleResult = (result) => {onResult(result);};if (type === 'ok') {buttons = (<Button variant="primary" onClick={ () => handleResult('ok') }>确定</Button>);}else if (type === 'yesno') {buttons = (<><Button variant="secondary" onClick={ () => handleResult('confirm') }>取消</Button><Button variant="primary" onClick={ () => handleResult('cancel') }>确定</Button></>)}return (<div><Modal show={showModal}><Modal.Header><Modal.Title>{title}</Modal.Title></Modal.Header><Modal.Body>{message}</Modal.Body><Modal.Footer>{buttons}</Modal.Footer></Modal></div>);
}export default MessageBox;
测试:
function App() {const handleResult = (result) => {console.log(result);};return (<div><MessageBox showModal={true} title="我是标题" message="Hello World" type="ok" onResult={handleResult} /></div>);
}
HTML 代码部分简化完成。这下代码短了不少。
现在如果想要正常使用消息框,还需要自己定义 showModal
状态并绑定 onResult
事件控制消息框的显示隐藏。下面我们来简化 JS 调用部分。
1. useContext
首先可以考虑全局都只放一份模态框的代码到某个位置,然后要用的时候都修改这一个模态框即可。这样就不用每次都写一个 <MessageBox ... />
了。
为了能在任意地方都访问到模态框,可以考虑用 Context 进行跨级通信。
把“修改模态框内容 + 处理隐藏”这部分封装成一个函数 show(),然后通过 Context 暴露出去。
import { useState, createContext, useRef, useContext } from "react";
import MessageBoxBase from "./MessageBox";const MessageBoxContext = createContext(null);function MessageBoxProvider(props) {let [showModal, setShowModal] = useState(false);let [title, setTitle] = useState('');let [message, setMessage] = useState('');let [type, setType] = useState(null);let resolveRef = useRef(null); // 因为与 UI 无关,用 ref 不用 stateconst handleResult = (result) => {resolveRef.current(result);setShowModal(false);};const show = (title, message, type) => {setTitle(title);setMessage(message);setType(type);setShowModal(true);return new Promise((resolve, reject) => {resolveRef.current = resolve;});};return (<MessageBoxContext.Provider value={show}><MessageBoxBasetitle={title}message={message}type={type}showModal={showModal}onResult={handleResult}/>{props.children}</MessageBoxContext.Provider>);
}export { MessageBoxProvider, MessageBoxContext };
使用:
index.js
root.render(<React.StrictMode><MessageBoxProvider><App /></MessageBoxProvider></React.StrictMode>
);
App.js
function App() {let msgBox = useContext(MessageBoxContext);const handleClick = async () => {let result = await msgBox('我是标题', 'Hello World', 'yesno');console.log(result);if (result === 'yes') {alert('yes');} else if (result === 'no') {alert('no');}};return (<div><Button variant="primary" onClick={handleClick}>弹窗1</Button></div>);
}
为了方便使用,可以在 useContext
之上再套一层:
/** * 以 Context 方式使用 MessageBox。* @return {(title: string, message: string, type: string) => Promise<string>}*/
function useMessageBox() {return useContext(MessageBoxContext);
}
这样封装使用起来是最简单的,只需要 useMessageBox 然后直接调函数即可显示消息框。
但是缺点显而易见,只能同时弹一个消息框,因为所有的消息框都要共享一个模态框。
2. Hook
为了解决上面只能同时弹一个框的问题,我们可以考虑取消全局只有一个对话框的策略,改成每个要用的组件都单独一个对话框,这样就不会出现冲突的问题了。
即将模态框组件和状态以及处理函数都封装到一个 Hook 里,每次调用这个 Hook 都返回一个组件变量和 show 函数,调用方只需要把返回的组件变量渲染出来,然后调用 show 即可。
import React, { useMemo } from "react";
import { useState, createContext, useRef } from "react";
import MessageBoxBase from "./MessageBox";/*** 以 Hook 方式使用消息框。* @returns {[MessageBox, show]} [MessageBox, show]* @example* const [MessageBox, show] = useMessageBox(); * return (* <MessageBox />* <button onClick={() => show('title', 'message', 'ok')} >show</button>* );*/
function useMessageBox() {let [title, setTitle] = useState('');let [message, setMessage] = useState('');let [type, setType] = useState(null);let [showDialog, setShowDialog] = useState(false);let resolveRef = useRef(null);const handleResult = (result) => {resolveRef.current(result);setShowDialog(false);};const MessageBox = useMemo(() => { // 也可以不用 useMemo 直接赋值 JSX 代码return (<MessageBoxBasetitle={title}message={message}type={type}showModal={showDialog}onResult={handleResult}/>);}, [title, message, type, showDialog]);const show = (title, message, type) => {setTitle(title);setMessage(message);setType(type);setShowDialog(true);return new Promise((resolve, reject) => {resolveRef.current = resolve;});};return [MessageBox, show];
}export default useMessageBox;
App.js
function App() {const [MessageBox, show] = useMessageBox();return (<div>{MessageBox}<button onClick={ () => show('title', 'message', 'ok') }>HookShow1</button><button onClick={ () => show('title', 'message', 'yesno') }>HookShow2</button></div>);
}
3. forwardRef + useImperativeHandle
上面我们都是封装成 show() 函数的形式。对于简单的消息框,这种调用方式非常好用。但是如果想要显示复杂的内容(例如 HTML 标签)就有些麻烦了。
这种情况可以考虑不封装 HTML 代码,HTML 代码让调用者手动编写,我们只封装控制部分的 JS 代码,即 showModal
状态和回调函数。
如果是类组件,可以直接添加一个普通的成员方法 show(),然后通过 ref 调用这个方法。但是现在我们用的是函数式组件,函数式组件想要使用 ref 需要使用 forwardRef
和 useImperativeHandle
函数,具体见这里。
import { useImperativeHandle, useRef, useState } from "react";
import MessageBox from "./MessageBox";
import { forwardRef } from "react";function MessageBoxRef(props, ref) {let [showModal, setShowModal] = useState(false);let resolveRef = useRef(null);function handleResult(result) {setShowModal(false);resolveRef.current(result);}// ref 引用的对象将会是第二个参数(回调函数)的返回值useImperativeHandle(ref, () => ({show() {setShowModal(true);return new Promise((resolve, reject) => {resolveRef.current = resolve;});}}), []); // 第三个参数为依赖,类似于 useEffect()return <MessageBox {...props} showModal={showModal} onResult={handleResult} />;
}export default forwardRef(MessageBoxRef);
使用的时候只需要创建一个 ref,然后 ref.current.show() 即可。
App.js
function App() {const messageBoxRef = useRef();return (<div><MessageBoxRef ref={messageBoxRef} title="标题" message="内容" /><button onClick={ () => messageBoxRef.current.show() }>RefShow</button></div>);
}
相关文章:

【React】封装一个好用方便的消息框(Hooks Bootstrap 实践)
引言 以 Bootstrap 为例,使用模态框编写一个简单的消息框: import { useState } from "react"; import { Modal } from "react-bootstrap"; import Button from "react-bootstrap/Button"; import bootstrap/dist/css/b…...

tomcat10部署踩坑记录-公网IP和服务器系统IP搞混
1. 服务器基本条件 使用的阿里云服务器,镜像系统是Ubuntu16.04java version “17.0.11” 2024-04-16 LTS装的是tomcat10.1.24阿里云服务器安全组放行了:8080端口 服务器防火墙关闭: 监听情况和下图一样: tomcat正常启动ÿ…...

探索Sass:Web开发的强大工具
在现代Web开发中,CSS(层叠样式表)作为前端样式设计的核心技术,已经发展得非常成熟。然而,随着Web应用的复杂性不断增加,传统的CSS书写方式逐渐暴露出一些不足之处,如代码冗长、难以维护、缺乏编程功能等。为了解决这些问题,Sass(Syntactically Awesome Stylesheets)应…...

vue组件之间的通信方式有哪些
在开发过程中,数据传输是一个核心的知识点,掌握了数据传输,相当于掌握了80%的内容。 Vue.js 提供了多种组件间的通信方式,这些方式适应不同的场景和需求。下面是4种常见的通信方式: 1. Props & Events (父子组件通…...

111、二叉树的最小深度
给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 题解:找出最小深度也就是找出根节点相对所有叶子结点的最小高度,在这也表明了根节点的高度是变化的,相对不同的叶子结点有不同的高度。…...

SpringBoot3依赖管理,自动配置
文章目录 1. 项目新建2. 相关pom依赖3. 依赖管理机制导入 starter 所有相关依赖都会导入进来为什么版本号都不用写?如何自定义版本号第三方的jar包 4. 自动配置机制5. 核心注解 1. 项目新建 直接建Maven项目通过官方提供的Spring Initializr项目创建 2. 相关pom依…...

音视频开发17 FFmpeg 音频解码- 将 aac 解码成 pcm
这一节,接 音视频开发12 FFmpeg 解复用详情分析,前面我们已经对一个 MP4文件,或者 FLV文件,或者TS文件进行了 解复用,解出来的 视频是H264,音频是AAC,那么接下来就要对H264和AAC进行处理,这一节…...

vue2中封装图片上传获取方法类(针对后端返回的数据不是图片链接,只是图片编号)
在Vue 2中实现商品列表中带有图片编号,并将返回的图片插入到商品列表中,可以通过以下步骤完成: 在Vue组件的data函数中定义商品列表和图片URL数组。 创建一个方法来获取每个商品的图片URL。 使用v-for指令在模板中遍历商品列表,并…...

【C++面向对象编程】(二)this指针和静态成员
文章目录 this指针和静态成员this指针静态成员 this指针和静态成员 this指针 C中类的成员变量和成员函数的存储方式有所不同: 成员变量:对象的成员变量直接作为对象的一部分存储在内存中。成员函数:成员函数(非静态成员函数&am…...

最大矩形问题
柱状图中最大的矩形 题目 分析 矩形的面积等于宽乘以高,因此只要能确定每个矩形的宽和高,就能计算它的面积。如果直方图中一个矩形从下标为 i 的柱子开始,到下标为 j 的柱子结束,那么这两根柱子之间的矩形(含两端的柱…...

LeetCode62不同路径
题目描述 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。问总共有多少条不同的路径? …...

GNU Radio实现OFDM Radar
文章目录 前言一、GNU Radio Radar Toolbox编译及安装二、ofdm radar 原理讲解三、GNU Radio 实现 OFDM Radar1、官方提供的 grc①、grc 图②、运行结果 2、修改后的便于后续可实现探测和通信的 grc①、grc 图②、运行结果 四、资源自取 前言 本文使用 GNU Radio 搭建 OFDM Ra…...

东方博宜1760 - 整理抽屉
题目描述 期末考试即将来临,小T由于同时肩负了学习、竞赛、班团活动等多方面的任务,一直没有时间好好整理他的课桌抽屉,为了更好地复习,小T首先要把课桌抽屉里的书分类整理好。 小T的抽屉里堆着 N 本书,每本书的封面上…...

react快速开始(四)-之Vite 还是 (Create React App) CRA? 用Vite创建项目
文章目录 react快速开始(四)-之Vite 还是 (Create React App) CRA? 用Vite创建项目背景Vite 和 (Create React App) CRAVite?Vite 是否支持 TypeScript? 用Vite创建react项目参考 react快速开始(四)-之Vite 还是 (Create React App) CRA? 用Vite创建项…...

使用python绘制核密度估计图
使用python绘制核密度估计图 核密度估计图介绍效果代码 核密度估计图介绍 核密度估计(Kernel Density Estimation,KDE)是一种用于估计数据概率密度函数的非参数方法。与直方图不同,KDE 可以生成平滑的密度曲线,更好地…...

5. MySQL 运算符和函数
文章目录 【 1. 算术运算符 】【 2. 逻辑运算符 】2.1 逻辑非 (NOT 或者 !)2.2 逻辑与运算符 (AND 或者 &&)2.3 逻辑或 (OR 或者 ||)2.4 异或运算 (XOR) 【 3. 比较运算符 】3.1 等于 3.2 安全等于运算符 <>3.3 不等于运算符 (<> 或者 !)3.4 小于等于运算符…...

Linux学习之vi文本编辑器的使用
天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。…...

【数据结构】链表与顺序表的比较
不同点: 顺序表和链表是两种常见的数据结构,他们的不同点在于存储方式和插入、删除操作、随机访问、cpu缓存利用率等方面。 一、存储方式不同: 顺序表: 顺序表的存储方式是顺序存储,在内存中申请一块连续的空间,通…...

dart 基本语法
//入口方法 main() 或 void main() //数据类型 原生数据类型 String int double bool null 注意:String 包函 ‘’ “” ‘’’ ‘’’ 三种形式复杂数据类型 list Set Map自定义数据类型 class inheritance动态数据类型 var 注:dart 是静态类型语言&a…...

【经验分享】嵌入式入坑经历(选段)
文章目录 你现在的工作中所用到的专业知识有哪些呢?为什么想转行了?后来为什么从事了嵌入式行业呢?你对嵌入式的兴趣是何时培养起来的?你是怎么平衡兴趣爱好和工作的关系的?平时做的事情对你现在的工作有哪些帮助?对于有志学习嵌入式开发的在校大学生…...

Docker面试整理-Docker与虚拟机的区别是什么?
Docker 容器和传统的虚拟机(VM)都是提供隔离的运行环境以部署和运行应用程序的技术,但它们在架构和性能上存在显著的不同。了解这些差异可以帮助你选择最适合特定需求的解决方案: 基础架构:虚拟机:每个虚拟机都包括完整的操作系统、应用程序、必需的库和二进制文件,运行在…...

Java:JDK8 GC中ParNew和CMS的问题说明
JDK8中常用如下的垃圾收集器,它们分别运用在年轻代和老年代: ParNew : 年轻代垃圾收集器,多线程,采用标记—复制算法。 CMS:老年代的收集器,全称(Concurrent Mark and Sweep)&#…...

学单片机前先学什么?
先学c语言和数字电路 这里默认你说的单片机是51单片机,通过你的问题,我猜你的单片机应该还没有入门,如果是入门的话,一般都是从51单片机开始的。刚好我有一些资料,是我根据网友给的问题精心整理了一份「单片机的资料从…...

数据可视化:Matplotlib 与 Seaborn
数据可视化是数据分析中至关重要的一部分,它能帮助我们直观地理解数据的分布、趋势和关系。Python 中,Matplotlib 和 Seaborn 是两个最常用的可视化库。本文将详细介绍如何使用 Matplotlib 和 Seaborn 进行数据可视化,包括基本图形、图形定制…...

【linux】自定义快捷命令/脚本
linux自定义快捷命令 场景自定义命令自定义脚本 场景 深度学习经常要切换到自己环境,conda activate mmagic,但是又不想每次重复打这么多字,想使用快捷命令直接切换。 自定义命令 使用别名(alias)或自定义脚本来创建…...

使用onnxruntime加载YOLOv8生成的onnx文件进行目标检测
在网上下载了60多幅包含西瓜和冬瓜的图像组成melon数据集,使用 LabelMe 工具进行标注,然后使用 labelme2yolov8 脚本将json文件转换成YOLOv8支持的.txt文件,并自动生成YOLOv8支持的目录结构,包括melon.yaml文件,其内容…...

QT 信号和槽 一对多关联示例,一个信号,多个槽函数响应,一个信号源如何绑定多个槽函数
在窗体里放置一个单行文本编辑控件(QLineEdit)、一个标签控件(QLabel)和一个文本浏览控件(QTextBrowser),在单行文 本编辑控件里的文本被编辑时,标签控件和文本浏览控件都会同步显示…...

C++ AVL树 详细讲解
目录 一、AVL树的概念 二、AVL树的实现 1.AVL树节点的定义 2.AVL树的插入 3.AVL树的旋转 4.AVL树的验证 三、AVL树的性能 四、完结撒❀ 一、AVL树的概念 二叉搜索树虽可以缩短查找的效率,但 如果数据有序或接近有序二叉搜索树将退化为单支树,查 …...

Faster R-CNN:端到端的目标检测网络
本文回顾了由微软研究人员开发的 Faster R-CNN 模型。Faster R-CNN 是一种用于物体检测的深度卷积网络,在用户看来,它是一个单一的、端到端的统一网络。该网络可以准确快速地预测不同物体的位置。为了真正理解 Faster R-CNN,我们还必须快速概…...

如何给 MySQL 表和列授予权限?(官方版)
目录 授予表级别权限 授予列级别权限 如何给MySQL表和列授予权限是MySQL数据操作中非常重要的步骤,也是企业级使用MySQL数据库的起步点,以下分别参照官方教程整理的MySQL数据库的权限操作。 以下的语句可以直接使用MySQL的命令行进行操作(如何…...