关于转发多个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 在设计上并非尽如人意。它也有很多令人难以理解的地方。



相关文章
|
3月前
|
前端开发 JavaScript 开发者
颠覆传统:React框架如何引领前端开发的革命性变革
【10月更文挑战第32天】本文以问答形式探讨了React框架的特性和应用。React是一款由Facebook推出的JavaScript库,以其虚拟DOM机制和组件化设计,成为构建高性能单页面应用的理想选择。文章介绍了如何开始一个React项目、组件化思想的体现、性能优化方法、表单处理及路由实现等内容,帮助开发者更好地理解和使用React。
119 9
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
DeepSeek Artifacts:在线实时预览的前端 AI 编程工具,基于DeepSeek V3快速生成React App
DeepSeek Artifacts是Hugging Face推出的免费AI编程工具,基于DeepSeek V3,支持快速生成React和Tailwind CSS代码,适合快速原型开发和前端组件构建。
929 39
DeepSeek Artifacts:在线实时预览的前端 AI 编程工具,基于DeepSeek V3快速生成React App
|
3月前
|
监控 前端开发 数据可视化
3D架构图软件 iCraft Editor 正式发布 @icraft/player-react 前端组件, 轻松嵌入3D架构图到您的项目,实现数字孪生
@icraft/player-react 是 iCraft Editor 推出的 React 组件库,旨在简化3D数字孪生场景的前端集成。它支持零配置快速接入、自定义插件、丰富的事件和方法、动画控制及实时数据接入,帮助开发者轻松实现3D场景与React项目的无缝融合。
280 8
3D架构图软件 iCraft Editor 正式发布 @icraft/player-react 前端组件, 轻松嵌入3D架构图到您的项目,实现数字孪生
|
3月前
|
前端开发 JavaScript 开发者
使用React和Redux构建高效的前端应用
使用React和Redux构建高效的前端应用
71 1
|
3月前
|
前端开发 JavaScript Android开发
前端框架趋势:React Native在跨平台开发中的优势与挑战
【10月更文挑战第27天】React Native 是跨平台开发领域的佼佼者,凭借其独特的跨平台能力和高效的开发体验,成为许多开发者的首选。本文探讨了 React Native 的优势与挑战,包括跨平台开发能力、原生组件渲染、性能优化及调试复杂性等问题,并通过代码示例展示了其实际应用。
99 2
|
3月前
|
前端开发 JavaScript 算法
探索现代前端框架——React 的性能优化策略
探索现代前端框架——React 的性能优化策略
44 0
|
3月前
|
前端开发 JavaScript API
探索现代前端框架——React 的性能优化策略
探索现代前端框架——React 的性能优化策略
47 0
|
3月前
|
前端开发 Android开发 开发者
前端框架趋势:React Native在跨平台开发中的优势与挑战
【10月更文挑战第26天】近年来,React Native凭借其跨平台开发能力在移动应用开发领域迅速崛起。本文将探讨React Native的优势与挑战,并通过示例代码展示其应用实践。React Native允许开发者使用同一套代码库同时构建iOS和Android应用,提高开发效率,降低维护成本。它具备接近原生应用的性能和用户体验,但也面临平台差异、原生功能支持和第三方库兼容性等挑战。
75 0
|
前端开发 JavaScript 中间件
react前端框架dva(四)
这篇文档梳理了基于 dva-cli 使用 dva 的最小知识集,让你可以用最少的时间掌握创建类似 dva-hackernews 的全部知识,并且不需要掌握额外的冗余知识。
2913 0
|
前端开发 API 测试技术

热门文章

最新文章

  • 1
    【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 2
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 3
    【05】flutter完成注册页面完善样式bug-增加自定义可复用组件widgets-严格规划文件和目录结构-规范入口文件-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 4
    详解智能编码在前端研发的创新应用
  • 5
    巧用通义灵码,提升前端研发效率
  • 6
    智能编码在前端研发的创新应用
  • 7
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
  • 8
    【07】flutter完成主页-完成底部菜单栏并且做自定义组件-完整短视频仿抖音上下滑动页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 9
    抛弃node和vscode,如何用记事本开发出一个完整的vue前端项目
  • 10
    大前端之前端开发接口测试工具postman的使用方法-简单get接口请求测试的使用方法-简单教学一看就会-以实际例子来说明-优雅草卓伊凡