在 React 中,ref 不受 React 的状态管理(State)机制控制。也就是说当 ref 的内容发生变更的时候不会触发重新渲染。
React 状态管理和渲染机制
React 的核心是响应式渲染,即当组件的状态(state
)或属性(props
)发生变化时,React 会重新渲染组件。这是 React 的工作原理:通过 state
和 props
的变化驱动 UI 的更新。
- 当调用
setState
时,React 会比较新旧状态,决定是否需要重新渲染组件。 props
的变化也会触发组件的重新渲染。
这意味着任何通过 state
或 props
来管理的数据都是与 UI 渲染相关联的,React 会根据这些数据的变化决定重新渲染哪些部分。
除了 state
和 props
,React Context
也可以用于管理组件树中深层嵌套组件间的共享状态。Context
允许在不显式传递 props
的情况下,在父组件和子组件之间传递数据。当 Context
的值发生变化时,所有订阅该 Context
的组件都会重新渲染。因此,像 state
和 props
一样,Context
也是影响组件重新渲染的关键因素。
useRef 的设计目标
useRef
的设计目标是提供一种访问 DOM 或保存组件实例的引用,而不是参与到 React 的状态管理中。它的主要作用可以分为两个方面:
- 保存对 DOM 元素或组件实例的引用:这是
ref
最常见的用途,用于访问原生 DOM 元素或组件实例,而无需通过 React 的虚拟 DOM 进行间接操作。 - 保存跨渲染周期的变量:
useRef
还可以用来存储组件内的某些数据,但这些数据的变化不会触发组件重新渲染。
为什么 useRef
不受 React 管理?
useRef
的值是持久化的,不会触发渲染
useRef
的特殊之处在于它的 .current
属性可以在组件的整个生命周期内保持稳定。当你更新 ref
的 .current
值时,React 并不会重新渲染组件。原因如下:
useRef
返回的引用对象在每次渲染时都是同一个。这意味着即使组件重新渲染,useRef
的值不会被重置。useRef
的.current
属性的修改不会触发 React 的重新渲染,因为 React 将其视为与 UI 渲染无关的数据。
避免不必要的重新渲染
React 的重新渲染机制是基于状态变化的。如果某些数据的变化与组件的 UI 不相关,那让这些数据进入 React 的状态管理系统是没有必要的。
useRef
的作用正是为了存储那些不影响 UI 的数据,比如某些计时器、缓存值、滚动位置等。如果将这些数据放入state
中,每次数据变化都会触发重新渲染,影响性能。而使用useRef
,可以避免这些不必要的重新渲染。
useRef
的生命周期
useRef
在组件的整个生命周期中是持续的。当你在一次渲染中设置了 ref.current
的值,之后的所有渲染都会保持这个值。而且,useRef
的值在组件重新渲染时也不会重置(除非组件完全卸载)。这是 useRef
和 state
的一个显著区别:
state
每次变化都会导致重新渲染。useRef
的值不会随渲染变化,即便更新了ref.current
,React 也不会触发重新渲染。
useRef
的使用场景与 state
不同
通常,你会使用 state
来管理那些需要触发 UI 更新的数据,比如表单输入的值、用户操作引发的状态变化等。而 useRef
则用于存储不需要触发重新渲染的变量,比如:
- 存储组件实例或 DOM 元素的引用。
- 存储计数器、定时器 ID 或其他跨渲染的值。
- 保存上一次渲染的一些状态以供对比(例如保存上一次的 props 值)。
示例代码:useRef
不触发渲染
import React, { useState, useRef } from "react";
function MyComponent() {
const [count, setCount] = useState(0); // 触发渲染
const refValue = useRef(0); // 不触发渲染
const handleClick = () => {
setCount(count + 1); // 更新 state,触发渲染
refValue.current += 1; // 更新 ref,不触发渲染
console.log("refValue current:", refValue.current); // 控制台输出变化,但 UI 不更新
};
return (
<div>
<p>State count: {count}</p>
<p>Ref value: {refValue.current}</p>
<button onClick={handleClick}>点击我</button>
</div>
);
}
export default MyComponent;
在上面的代码中,每次点击按钮时,state
会触发组件重新渲染,ref
的值会更新但不会导致渲染。所以你会看到 state
的值更新并反映在 UI 上,而 ref
的变化只体现在控制台输出中。
总结
useRef
不受 React 状态管理的原因是它的设计目标是用于存储那些不需要触发渲染的数据。- 它的变化不会引发组件重新渲染,因为
useRef
的主要目的是跨渲染周期保持某些引用(如 DOM 元素或普通变量),而这些引用的变化与 React 的 UI 更新机制无关。