Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $YECBGYFECGEAFWHA as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2

Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $BBWFDDBHHYHDXXAB as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2
在React中通过Context管理Ref并处理焦点事件的实践指南_创想鸟

在React中通过Context管理Ref并处理焦点事件的实践指南

在React中通过Context管理Ref并处理焦点事件的实践指南

本文探讨了在React应用中,当通过Context将DOM元素的ref传递给子组件时,如何正确处理焦点事件。针对blur事件不冒泡导致无法在父元素上捕获子元素失焦的问题,文章提出了使用focusout事件作为解决方案,并提供了详细的代码示例和解释,以确保事件监听器的正确设置和清理。

理解React Context与Ref的结合应用

在react中,ref是访问dom节点或组件实例的一种方式。当我们需要在多个组件之间共享一个dom元素的ref时,react context提供了一种高效且简洁的机制。例如,一个父组件可能拥有一个dom元素的ref,并希望其子组件能够基于此ref执行某些操作,如添加事件监听器。

考虑以下场景:一个EditorProvider组件管理一个div元素的ref,并通过Context将其暴露给消费组件。

import React, { useRef, useContext, useMemo } from 'react';// 定义Context的类型interface EditorContextProps {    historyState: any; // 示例,具体类型根据实际需求定义    ref: React.RefObject;}// 创建Contextconst EditorContext = React.createContext(undefined);// 自定义Hook,方便消费Contextexport function useEditorContext(): EditorContextProps {    const context = useContext(EditorContext);    if (context === undefined) {        throw new Error('useEditorContext must be used within an EditorProvider');    }    return context;}// EditorProvider组件export default function EditorProvider({ children }: { children: React.ReactNode }) {    const ref = useRef(null); // 创建ref    const historyState = useMemo(() => ({ /* 初始状态 */ }), []); // 示例    const context = { historyState, ref };    return (                    
{/* 将ref绑定到DOM元素 */} {children}
);}

上述代码展示了如何通过EditorProvider将一个HTMLDivElement的ref共享出去。消费组件(例如一个Lexical插件)可以通过useEditorContext钩子获取到这个ref。

blur事件的局限性与focusout的优势

当消费组件尝试监听这个共享ref上的失焦事件时,一个常见的误区是使用blur事件。

import React, { useEffect, useCallback } from 'react';import { useEditorContext } from './EditorProvider'; // 假设路径function MyLexicalPlugin() {    const { ref } = useEditorContext();    const blurHandler = useCallback((event: FocusEvent) => {        console.log('Blurred');        // 在这里处理失焦逻辑    }, []);    useEffect(() => {        const element = ref.current;        if (element) {            // 错误示范:blur事件不冒泡            element.addEventListener('blur', blurHandler, false);        }        return () => {            if (element) {                element.removeEventListener('blur', blurHandler);            }        };    }, [ref.current, blurHandler]); // 依赖项中包含ref.current    return null; // 插件通常不渲染DOM}

上述代码中,blurHandler可能不会按预期触发,尤其是当焦点从ref所指向的div内部的某个子元素移出时。这是因为blur事件不会冒泡。这意味着,如果焦点从一个子元素移出,blur事件只会在该子元素上触发,而不会传播到其父元素(即我们通过ref引用的div)。

为了解决这个问题,我们应该使用focusout事件。focusout事件与blur事件类似,都表示元素失去焦点,但关键区别在于focusout事件会冒泡。当元素本身或其任何后代元素失去焦点时,focusout事件都会在元素上触发。这使得它非常适合在父元素上监听子元素的失焦行为。

使用focusout事件实现正确的失焦监听

将useEffect中的blur事件替换为focusout即可解决问题:

import React, { useEffect, useCallback } from 'react';import { useEditorContext } from './EditorProvider'; // 假设路径function MyLexicalPlugin() {    const { ref } = useEditorContext();    const blurHandler = useCallback((event: FocusEvent) => {        console.log('Blurred or focus moved out from a child element');        // 在这里处理失焦逻辑    }, []);    useEffect(() => {        const element = ref.current;        if (element) {            // 正确做法:使用focusout事件,它会冒泡            element.addEventListener('focusout', blurHandler);        } else {            return; // 如果element不存在,则不进行操作        }        // 清理函数:在组件卸载或依赖项改变时移除事件监听器        return () => {            if (element) { // 再次检查element是否存在,以防万一                element.removeEventListener('focusout', blurHandler);            }        };    }, [ref.current, blurHandler]); // 依赖项中包含ref.current和blurHandler    return null;}

代码解析与注意事项:

useCallback包裹事件处理函数: blurHandler被useCallback包裹,这确保了在ref.current不变的情况下,blurHandler的引用在每次渲染时保持稳定。这对于useEffect的依赖项优化非常重要,避免不必要的事件监听器重新注册。useEffect的依赖项:ref.current:当ref指向的DOM元素发生变化时(尽管这种情况在EditorProvider中通常不会发生,但作为最佳实践,应包含),useEffect会重新运行。blurHandler:虽然我们使用了useCallback,但如果blurHandler内部依赖了外部变量,并且这些变量发生变化,blurHandler的引用也会改变,此时useEffect需要重新运行以注册新的事件处理函数。清理函数: useEffect返回的函数是清理函数。它会在组件卸载时或useEffect的依赖项发生变化并重新执行之前运行。在这里,我们移除了之前添加的focusout事件监听器,防止内存泄漏。focusout vs focusin: 类似的,focusin事件是focus事件的冒泡版本。如果你需要在父元素上监听子元素的获取焦点行为,可以使用focusin。条件检查: 在useEffect内部,始终检查ref.current是否为null。在组件挂载的初始阶段,ref.current可能尚未被赋值。

总结

在React中,通过Context共享ref是管理复杂组件间DOM交互的强大模式。当需要监听这些共享ref上的焦点事件时,理解blur和focusout(或focus和focusin)之间的区别至关重要。focusout事件的冒泡特性使其成为在父元素上捕获子元素失焦行为的理想选择。结合useEffect和useCallback的正确使用,我们可以构建健壮且无内存泄漏的事件监听机制。

以上就是在React中通过Context管理Ref并处理焦点事件的实践指南的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1523398.html

赞 (0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
如何利用Node.js的Child Process模块执行外部命令或脚本?
上一篇 2025年12月20日 15:53:07
Chrome 扩展中 IndexedDB 性能异常及事件监听器误用的排查与解决
下一篇 2025年12月20日 15:53:19

相关推荐

  • Spring Boot @Nested 测试中属性覆盖与隔离策略

    Spring Boot @Nested 测试中属性覆盖与隔离策略Spring Boot @Nested 测试中属性覆盖与隔离策略Spring Boot @Nested 测试中属性覆盖与隔离策略Spring Boot @Nested 测试中属性覆盖与隔离策略

    本文深入探讨了在Spring Boot集成测试中,如何利用@Nested注解结合@TestPropertySource实现细粒度的属性配置和隔离。通过详细的示例代码,展示了外部测试类和嵌套测试类如何定义各自的属性集,以及这些属性在不同测试上下文中的继承与覆盖机制,从而确保测试环境的精确控制和独立性。…

    2026年9月24日 • 用户投稿
    000
  • ubuntu如何mount网络驱动器

    在ubuntu中挂载网络驱动器有多种方法,以下是一些常见的方法: 方法一:使用mount命令 确定网络驱动器的地址:例如,如果是Samba共享,地址可能是smb://server/share。如果是NFS共享,地址可能是nfs://server/share。安装必要的软件包:对于Samba共享,安装…

    2026年9月24日
    000
  • Java中双精度浮点数的小数位控制技巧

    Java中双精度浮点数的小数位控制技巧Java中双精度浮点数的小数位控制技巧Java中双精度浮点数的小数位控制技巧Java中双精度浮点数的小数位控制技巧

    本文深入探讨了在Java中有效控制double类型数值小数位数的方法。通过Math.round()函数结合乘除操作,可以实现数值本身的四舍五入并改变其精度;而String.format()则提供了灵活的字符串格式化功能,用于在不修改原始数值的情况下精确控制显示的小数位数。这两种方法分别适用于不同的业…

    2026年9月24日 • 用户投稿
    000
  • google浏览器怎么把网页保存为PDF_google浏览器网页保存为PDF方法

    使用Chrome将网页保存为PDF,首先按Ctrl+P进入打印界面,选择“另存为PDF”并调整设置后保存;也可通过F12打开开发者工具,截取指定元素或完整页面截图后转为PDF;还可安装“Save as PDF”等扩展程序实现更高质量的导出。 如果您希望将当前浏览的网页完整保存以便离线查看或分享,Go…

    2026年9月24日
    000
  • 如何在Linux中切换用户身份?

    Linux中切换用户主要用su和sudo命令;2. su切换用户需密码,su -可加载完整环境;3. sudo允许授权用户以root等身份执行命令而无需对方密码;4. 推荐使用sudo -i或sudo su -切换到root;5. 普通用户需加入sudo组或配置/etc/sudoers文件;6. 编…

    2026年9月24日
    200
  • 如何在mysql中升级高可用集群

    先确认版本兼容性、应用依赖及备份完整性,再按架构选择升级路径。对Group Replication或InnoDB Cluster采用滚动升级,先升从节点最后升主节点;MHA/Orchestrator架构先升备库再切换主库;PXC需停集群全量升级。替换二进制后启动实例并运行mysql_upgrade,…

    2026年9月24日
    100
  • PHP如何批量处理图片_PHP实现多张图片自动化处理

    批量处理图片时需循环读取并逐个处理,核心是使用scandir()获取文件列表,通过GD库或Imagick处理图像,每处理完一张用imagedestroy()释放内存以避免内存溢出;为提升效率可分批处理、优化算法、使用多进程或异步队列,并选用Intervention Image等高效第三方库。 批量处…

    2026年9月24日
    200
  • MySQL怎样处理SQL注入风险 参数化查询与特殊字符过滤方案

    MySQL怎样处理SQL注入风险 参数化查询与特殊字符过滤方案MySQL怎样处理SQL注入风险 参数化查询与特殊字符过滤方案MySQL怎样处理SQL注入风险 参数化查询与特殊字符过滤方案MySQL怎样处理SQL注入风险 参数化查询与特殊字符过滤方案

    参数化查询和特殊字符过滤是防止sql注入的有效方法。1. 参数化查询通过预处理语句将sql结构与数据分离,用户输入被视为参数,不会被解释为sql命令;2. 特殊字符过滤通过转义或拒绝单引号、双引号等危险字符来阻止攻击;3. 定期审查mysql安全配置,包括更新版本、限制权限、启用日志、使用防火墙和扫…

    2026年9月24日 • 用户投稿
    000
  • 减少PHP与MySQL数据库通信的延迟

    减少php与mysql数据库通信的延迟可以通过以下策略:1. 优化数据库查询,使用索引提升查询速度;2. 减少数据库连接次数,使用连接池管理连接;3. 查询优化,使用explain分析查询计划;4. 使用缓存,如redis,减少数据库查询次数。这些方法能显著提升应用性能,但需权衡利弊,确保系统稳定性…

    2026年9月24日
    000
  • 讯维解决KVM鼠标不同步

    讯维解决KVM鼠标不同步讯维解决KVM鼠标不同步讯维解决KVM鼠标不同步讯维解决KVM鼠标不同步

    使用网络kvm时,常遇到本地鼠标与远程界面光标位置不一致的问题,即鼠标不同步现象,严重影响操作流畅性。可通过优化鼠标同步设置、更新驱动程序或选用兼容性更强的设备来有效改善。 1、配置运行Windows 2000操作系统的服务器环境 2、调整鼠标相关参数 3、点击开始菜单,进入控制面板,选择“鼠标”进…

    2026年9月24日 • 用户投稿
    900
  • 如何分析Linux进程内存 pmap内存映射检查方法

    如何分析Linux进程内存 pmap内存映射检查方法如何分析Linux进程内存 pmap内存映射检查方法如何分析Linux进程内存 pmap内存映射检查方法如何分析Linux进程内存 pmap内存映射检查方法

    要分析linux进程的内存,特别是利用pmap工具,核心操作是获取目标进程pid后执行pmap -x 。1. 获取pid可通过ps aux | grep your_process_name;2. 执行pmap -x 命令查看扩展格式信息,包括address、kbytes、rss、dirty、mode…

    2026年9月24日 • 用户投稿
    200
  • PHP实时输出如何防止XSS攻击_PHP实时输出安全防范XSS攻击

    防止XSS攻击需坚持三重防护:首先对用户输入进行严格验证与白名单过滤,使用filter_var等函数校验数据格式;其次根据输出上下文进行恰当转义——HTML正文和属性用htmlspecialchars(),JavaScript变量用json_encode(),URL参数用urlencode();最后…

    2026年9月24日
    100
  • Java Optional.orElse与orElseGet区别

    orElse总是执行默认值计算,而orElseGet仅在Optional为空时调用Supplier获取,默认值构造 costly 时应优先使用orElseGet以避免性能浪费。 在 Java 8 引入的 Optional 类中,orElse 和 orElseGet 都用于在 Optional 值为空…

    2026年9月24日
    100
  • Java中接口常量和类常量的使用区别

    接口常量默认public static final,用于行为契约但易导致职责模糊;类常量可用不同访问修饰符,更适合封装和维护。现代Java推荐使用专用常量类、枚举、私有静态常量或配置文件管理常量,以提升代码清晰度与可维护性。 Java中接口常量和类常量,核心区别在于它们的定义位置和隐式属性。接口常量…

    2026年9月24日
    100
  • 处理PHP多线程的定时任务并行_优化php多线程怎么实现的定时任务执行

    PHP可通过多进程、消息队列等方式实现定时任务并行处理。1. 使用pthreads扩展(需ZTS支持)可在CLI环境实现多线程,但部署复杂;2. 利用pcntl_fork创建子进程是推荐方案,通过fork多个进程并行执行任务,适合CLI模式;3. 通过crontab同时触发多个独立脚本或使用exec…

    2026年9月24日
    200
  • 怎样处理C++中的野指针问题 空指针检测与防御性编程

    怎样处理C++中的野指针问题 空指针检测与防御性编程怎样处理C++中的野指针问题 空指针检测与防御性编程怎样处理C++中的野指针问题 空指针检测与防御性编程怎样处理C++中的野指针问题 空指针检测与防御性编程

    野指针难以发现是因为其指向已失效或非法内存,解引用会导致未定义行为。1. 初始化是关键防线,声明指针时必须赋初值或设为nullptr;2. 使用智能指针std::unique_ptr和std::shared_ptr可自动管理内存生命周期,避免手动delete遗漏;3. 防御性编程要求每次使用指针前进…

    2026年9月24日 • 用户投稿
    300
  • VSCode如何实现移动端调试 VSCode连接Android/iOS设备的技巧

    vscode本身不支持移动端调试,但可通过插件和工具间接实现。1. 调试android应用时,需开启设备开发者模式和usb调试,连接电脑后通过chrome浏览器访问chrome://inspect/#devices,使用chrome devtools调试webview;可配合vscode的debug…

    2026年9月24日
    000
  • 抖音粉丝LV0到LV6等级要卖多少钱,2025年抖音粉丝最新价格参考

    抖音达人带货等级从lv0到lv6级,都需要卖多少钱?很多新手朋友都不清楚带货等级是如何划分的额,也不清楚每个等级都需要多少交易额,相匹配的抖音粉丝数量是多少,接下来小编会带领大家详细了解下抖音粉丝带货等级的区别和2025年抖音最新的价格参考明细: 一,抖音粉丝等级划分标注和对应粉丝数量: 1,LV0…

    2026年9月24日
    100
  • VSCode如何通过Dev Containers开发 VSCode开发容器环境的搭建与使用

    vscode通过dev containers提供容器化开发环境,解决了“在我的机器上能运行”的问题。1. 安装docker并配置vscode访问;2. 安装remote – containers扩展;3. 创建.devcontainer文件夹和devcontainer.json文件;4.…

    2026年9月24日
    100
  • Laravel 表单多动作处理:区分同一路由下的提交操作

    本教程将详细介绍如何在 laravel 应用中,通过一个 html 表单的多个提交按钮触发不同的后端操作,而无需为每个操作创建单独的表单或路由。核心方法是为提交按钮添加 `name` 和 `value` 属性,然后在控制器中根据这些属性的值来判断执行哪种业务逻辑,从而实现如更新用户角色和删除用户等多…

    2026年9月24日
    000

发表回复

登录后才能评论
关注微信