返回

深入探讨 React Ref,揭秘其用法和源码玄机

前端

在 React 开发中,Ref 是一个重要的特性,它允许我们在组件实例中访问 DOM 节点或其他可变对象。在本篇文章中,我们将深入探讨 React Ref 的用法,并通过源码解析来揭示其内部运作机制。

理解 React Ref

Ref 本质上是一个指向 DOM 节点的引用,它可以帮助我们在组件的生命周期中访问和操作该节点。React 提供了两种创建 Ref 的方式:

  1. 创建 Ref 对象 (useRef) :使用 useRef 钩子创建可变的 Ref 对象,它在整个组件的生命周期中保持不变。
  2. 回调 Ref (callbackRef) :通过函数的形式传递 Ref,当组件挂载或更新时,将 DOM 节点或组件实例作为参数传递给函数。

Ref 的用法

Ref 可以用于各种场景,例如:

  • 访问 DOM 节点,以便手动操作或集成第三方库。
  • 存储组件实例的引用,以便在需要时调用其方法或获取其状态。
  • 跟踪滚动位置、输入焦点或其他动态信息。

源码解析

要了解 Ref 的内部运作机制,我们需要深入 React 源码。在 react-dom/cjs/react-dom.development.js 中,我们可以找到 useRefcreateRef 函数的实现:

export function useRef<T>(initialValue: T): {current: T | null};
export function createRef<T = any>(): {current: T | null};

可以看到,这两个函数都返回一个包含 current 属性的对象。current 属性存储着 Ref 的当前值,初始值为 null

对于回调 Ref,React 会在组件渲染时调用回调函数,并将 DOM 节点或组件实例作为参数传递。

回调 Ref 执行两次的原因

回调 Ref 执行两次的原因是 React 的生命周期机制。在 React 的生命周期中,渲染阶段会执行两次:

  • 第一次渲染 :生成虚拟 DOM,创建组件实例,但不会挂载到 DOM。
  • 第二次渲染 :将虚拟 DOM 更新为真实 DOM,挂载组件实例到 DOM。

在第一次渲染时,回调 Ref 会执行一次,但此时 DOM 节点尚未存在,因此 current 属性仍为 null。在第二次渲染时,回调 Ref 再次执行,此时 DOM 节点已存在,current 属性会被更新为指向该节点的引用。

Ref 的存储位置

React 不会将 Ref 存储在组件的 props 中,因为它想要保持组件的隔离性。如果将 Ref 存储在 props 中,那么外部代码就可以通过修改 props 来修改组件的内部状态,这可能会导致意想不到的行为。

Ref 的释放

Ref 的释放时机取决于 Ref 的类型:

  • useRef 产生的 Ref :在组件卸载时自动释放。
  • 回调 Ref :在组件卸载时或在组件的 componentWillUnmount 生命周期方法中手动调用 current = null 时释放。

结论

React Ref 是一个强大的工具,可以帮助我们在组件中访问和操作 DOM 节点或其他可变对象。通过理解 Ref 的用法和源码实现,我们可以更好地利用其功能,构建健壮且可维护的 React 应用程序。