React Ref 其实是这样的( 二 )
如果 ref 回调函数是以内联函数的方式定义的 , 在更新过程中它会被执行两次 , 第一次传入参数 null , 然后第二次会传入参数 DOM 元素 。 这是因为在每次渲染时会创建一个新的函数实例 , 所以 React 清空旧的 ref 并且设置新的 。 通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题 , 但是大多数情况下它是无关紧要的 。
后来的 React.createRefReact.createRef 的优点:
- 相对于 callback ref 而言 React.createRef 显得更加直观 , 避免了 callback ref 的一些理解问题 。
- 性能略低于 callback ref
- 能力上仍逊色于 callback ref , 例如上一节提到的组合问题 , createRef 也是无能为力的 。
- 当 ref 属性用于 HTML 元素时 , 构造函数中使用 React.createRef() 创建的 ref 接收底层 DOM 元素作为其 current 属性 。
- 当 ref 属性用于自定义 class 组件时 , ref 对象接收组件的挂载实例作为其 current 属性 。
- 默认情况下 , 你不能在函数组件上使用 ref 属性(可以在函数组件内部使用) , 因为它们没有实例:如果要在函数组件中使用 ref , 你可以使用 forwardRef(可与 useImperativeHandle 结合使用)或者可以将该组件转化为 class 组件 。
useRef 返回一个可变的 ref 对象 , 其 .current 属性被初始化为传入的参数(initialValue) 。 返回的 ref 对象在组件的整个生命周期内保持不变 。 并且 useRef 可以很方便地保存任何可变值 , 其类似于在 class 中使用实例字段的方式 。
正是由于这些特性 , useRef 和 createRef 出现了很大差异 。
可以运行下以下代码:
import React, { useState, useRef, useEffect } from "react";export default function App() {const [count, setCount] = useState(0);const latestCount = useRef(count);useEffect(() => {latestCount.current = count;});function handleAlertclick() {setTimeout(() => {alert("latestCount.current:" + latestCount.current + '.. count: ' + count);}, 2000);}return (当前count: {count}
setCount(count + 1)}>count + 1提示)}然后按照下面步骤进行操作:- 连续点击5次 count + 1 按钮
- 点击 提示 按钮
- 再点击完 提示 按钮后2秒内连续点击2次 count + 1 按钮
- 等待 alert 弹窗提示 。
具体原因可以参考 react 作者之一 dan 的个人博客 。 或者查看 React 函数式组件和类组件的区别 , 不是只有state和性能!
那么 useRef 真有那么很好用吗?并不是的 。 还有由于它上面的那个特性 , 问题还是不少的 。
你可以尝试跑一下下面这段代码 , 或者 点击这里查看
import React, { useRef, createRef, useState } from "react";import ReactDOM from "react-dom";function App() {const [renderIndex, setRenderIndex] = useState(1);const refFromUseRef = useRef();const refFromCreateRef = createRef();if (!refFromUseRef.current) {// 赋值操作refFromUseRef.current = renderIndex;}if (!refFromCreateRef.current) {// 赋值操作refFromCreateRef.current = renderIndex;}return (Current render index: {renderIndex}在refFromUseRef.current中记住的第一个渲染索引:{refFromUseRef.current}在refFromCreateRef.current中未能成功记住第一个渲染索引:{refFromCreateRef.current}setRenderIndex(prev => prev + 1)}>数值 + 1);}const rootElement = document.getElementById("root");ReactDOM.render(, rootElement);上面的案例中无论如何点击按钮 refFromUseRef.current 将始终为 1 , 而 renderIndex 和 refFromCreateRef.current 会伴随点击事件改变; 意想不到吧?因为:当 ref 对象内容发生变化时 , useRef 并不会通知你 。 变更 .current 属性不会引发组件重新渲染 。 如果想要在 React 绑定或解绑 DOM 节点的 ref 时运行某些代码 , 则需要使用 callback ref 来实现 。
总结下:
- useRef 可以获取 DOM ref
- useRef 可以获取最新的值
- useRef 内容发生改变并不会通知
Refs 转发是否需要将 DOM Refs 暴露给父组件?在极少数情况下 , 你可能希望在父组件中引用子节点的 DOM 节点 。 通常不建议这样做 , 因为它会打破组件的封装 , 但它偶尔可用于触发焦点或测量子 DOM 节点的大小或位置 。
如何将 ref 暴露给父组件?如果你使用 16.3 或更高版本的 React, 这种情况下我们推荐使用 ref 转发 。 Ref 转发使组件可以像暴露自己的 ref 一样暴露子组件的 ref 。
- 曝光|OPPO新机曝光,配置强悍颜值动人,“三金影后”为其代言
- 三星|高通骁龙875即将登场,三星Exynos 1080成其唯一对手
- 部署模式|5G toB大戏拉开帷幕,公网专用正当其时
- 小米|华为成俄罗斯线上销售最受欢迎品牌!小米紧随其后
- 中企|这和不要脸有何区别限制中企发展,还想要求中方给其“开后门”
- 欺诈和滥用|美国最高法院审理黑客法及其限制的案件
- 经济损失及|爱奇艺起诉马上玩App分时出租其平台VIP帐号,获赔300万元
- 替代|Firefox仍是市面上替代Chromium浏览器的唯一先进选择
- 深陷|信贷产品当道,年轻人深陷其中,“花呗们”该不该背锅?
- 黑科技|又一产品被央视曝光,披着“黑科技”外衣,其实在收割智商税
