关于转发多个Ref,90%前端都不知道的React useImperativeHandle Hook

简介: 关于转发多个Ref,90%前端都不知道的React useImperativeHandle Hook

为什么 ref 不属于 props,反而需要 forwardRef 呢?


当我们有一个非常简单的表单组件,伪代码如下:


const Form = () => {
  return <form>
    <label for="name"></label>
    <input type="text" id="name"></input>
    <button>submit</button>
  </form>
}

我想在外部直接获取 button 的 DOM 引用,该怎么做呢?

理想状态是,我们传递一个 ref 属性。


const Page = () => {
  const buttonRef = useRef()
  return <Form ref={buttonRef} />
}

然后在 Form 组件中接收并绑定。


const Form = ({ ref }) => {
  return <form>
    <label for="name"></label>
    <input type="text" id="name"></input>
    <button ref={ref}>submit</button>
  </form>
}

但是很遗憾,React 并没有这样做。

对应地,React 发明了 forwardRef 这种让代码变得难以阅读的用法。


const Form = forwardRef((props, ref) => {
  return <form>
    <label for="name"></label>
    <input type="text" id="name"></input>
    <button ref={ref}>submit</button>
  </form>
})

为什么要这样用呢?因为 ref 不仅仅只是做 DOM 节点引用的,它的作用是「不会重新渲染的状态」。


为什么 ref 不是 refs 呢?


接下来,我的需求变了,我需要获取 input 的 DOM 引用,该怎么做呢?

最简单的方法是把 ref 绑定到 form 元素上,然后通过 form 元素的 DOM API 去获取子元素。

但这样似乎很不 React。

当然还有一个办法,将 ref 传递一个对象。

像这样:


const Page = () => {
  const buttonRef = useRef()
  const inputRef = useRef()
  return <Form ref={{ buttonRef, inputRef}} />
}

然后子组件的代码这样写:


const Form = forwardRef((props, ref) => {
  return <form>
    <label for="name"></label>
    <input ref={ref.inputRef} type="text" id="name"></input>
    <button ref={ref.buttonRef}>submit</button>
  </form>
})

但这样又和 TypeScript 无法一起使用了。

为什么?因为 ref 的类型应该是一个 callback 或者是一个具有 current 属性的对象。

但是,其实只需要把 ref 改为 refs,就没有这么多烦恼了。

关于这一点,React 开发团队并没有什么解释,我认为是它们设计上的失误。


什么是 useImperativeHandle Hooks?


相信这个 API 对大多数 React 工程师来说都没有接触过。

它是做什么用的?从字面意思似乎是在做命令式编程时用的。React 官方的说法是,它需要暴露给父组件 DOM 实例时使用,一般要和 forwardRef 一起使用。

这个 Hooks 该怎么用呢?非常简单。

父组件不变,仍然传递一个 ref。


const Page = () => {
  const ref = useRef()
  return <Form ref={ref} />
}

子组件要发生一些变化了。


const Form = ({ ref }) => {
  const labelRef = useRef();
  const inputRef = useRef();
  const buttonRef = useRef();
  useImperativeHandle(ref, () => ({
    get label() {
      return labelRef.current;
    },
    get input() {
      return inputRef.current;
    },
    get button() {
      return buttonRef.current;
    },
  }))
  return <form>
    <label ref={labelRef} for="name"></label>
    <input ref={inputRef} type="text" id="name"></input>
    <button ref={buttonRef}>submit</button>
  </form>
}

在父组件中访问子组件的 DOM 实例时:


ref.current.input
ref.current.button


总结


可以看到,React 在设计上并非尽如人意。它也有很多令人难以理解的地方。



相关文章
|
2天前
|
前端开发 JavaScript API
React、Vue.js 和 Angular前端三大框架对比与选择
前端框架是用于构建用户界面的工具和库,它提供组件化结构、数据绑定、路由管理和状态管理等功能,帮助开发者高效地创建和维护 web 应用的前端部分。常见的前端框架如 React、Vue.js 和 Angular,能够提高开发效率并促进团队协作。
13 4
|
15天前
|
前端开发 JavaScript 开发者
Express.js与前端框架的集成:React、Vue和Angular的示例与技巧
本文介绍了如何将简洁灵活的Node.js后端框架Express.js与三大流行前端框架——React、Vue及Angular进行集成,以提升开发效率与代码可维护性。文中提供了详细的示例代码和实用技巧,展示了如何利用Express.js处理路由和静态文件服务,同时在React、Vue和Angular中构建用户界面,帮助开发者快速掌握前后端分离的开发方法,实现高效、灵活的Web应用构建。
31 3
|
14天前
|
JavaScript 前端开发 API
如何在前端开发中有效管理状态:React vs. Vue
在现代前端开发中,状态管理是一个关键因素,它直接影响到应用的性能和可维护性。React 和 Vue 是当前最流行的前端框架,它们在状态管理方面各有优势和劣势。本文将深入探讨 React 和 Vue 在状态管理中的不同实现,分析它们的优缺点,并提供实际应用中的最佳实践,以帮助开发者选择最适合他们项目的解决方案。
|
28天前
|
前端开发 Java Spring
Spring与Angular/React/Vue:当后端大佬遇上前端三杰,会擦出怎样的火花?一场技术的盛宴,你准备好了吗?
【8月更文挑战第31天】Spring框架与Angular、React、Vue等前端框架的集成是现代Web应用开发的核心。通过RESTful API、WebSocket及GraphQL等方式,Spring能与前端框架高效互动,提供快速且功能丰富的应用。RESTful API简单有效,适用于基本数据交互;WebSocket支持实时通信,适合聊天应用和数据监控;GraphQL则提供更精确的数据查询能力。开发者可根据需求选择合适的集成方式,提升用户体验和应用功能。
61 0
|
28天前
|
前端开发 Java JSON
Struts 2携手AngularJS与React:探索企业级后端与现代前端框架的完美融合之道
【8月更文挑战第31天】随着Web应用复杂性的提升,前端技术日新月异。AngularJS和React作为主流前端框架,凭借强大的数据绑定和组件化能力,显著提升了开发动态及交互式Web应用的效率。同时,Struts 2 以其出色的性能和丰富的功能,成为众多Java开发者构建企业级应用的首选后端框架。本文探讨了如何将 Struts 2 与 AngularJS 和 React 整合,以充分发挥前后端各自优势,构建更强大、灵活的 Web 应用。
40 0
|
28天前
|
前端开发 Java UED
JSF 面向组件开发究竟藏着何种奥秘?带你探寻可复用 UI 组件设计的神秘之路
【8月更文挑战第31天】在现代软件开发中,高效与可维护性至关重要。JavaServer Faces(JSF)框架通过其面向组件的开发模式,提供了构建复杂用户界面的强大工具,特别适用于设计可复用的 UI 组件。通过合理设计组件的功能与外观,可以显著提高开发效率并降低维护成本。本文以一个具体的 `MessageComponent` 示例展示了如何创建可复用的 JSF 组件,并介绍了如何在 JSF 页面中使用这些组件。结合其他技术如 PrimeFaces 和 Bootstrap,可以进一步丰富组件库,提升用户体验。
44 0
|
28天前
|
开发者 安全 UED
JSF事件监听器:解锁动态界面的秘密武器,你真的知道如何驾驭它吗?
【8月更文挑战第31天】在构建动态用户界面时,事件监听器是实现组件间通信和响应用户操作的关键机制。JavaServer Faces (JSF) 提供了完整的事件模型,通过自定义事件监听器扩展组件行为。本文详细介绍如何在 JSF 应用中创建和使用事件监听器,提升应用的交互性和响应能力。
20 0
|
28天前
|
开发者 自然语言处理 存储
语言不再是壁垒:掌握 JSF 国际化技巧,轻松构建多语言支持的 Web 应用
【8月更文挑战第31天】JavaServer Faces (JSF) 框架提供了强大的国际化 (I18N) 和本地化 (L10N) 支持,使开发者能轻松添加多语言功能。本文通过具体案例展示如何在 JSF 应用中实现多语言支持,包括创建项目、配置语言资源文件 (`messages_xx.properties`)、设置 `web.xml`、编写 Managed Bean (`LanguageBean`) 处理语言选择,以及使用 Facelets 页面 (`index.xhtml`) 显示多语言消息。通过这些步骤,你将学会如何配置 JSF 环境、编写语言资源文件,并实现动态语言切换。
28 0
|
1月前
|
前端开发 JavaScript UED
React 基础与实践 | 青训营笔记
React 基础与实践 | 青训营笔记
42 0
|
2月前
|
前端开发 JavaScript Java
React 速通笔记
【7月更文挑战第17天】
37 1