useRef
是一个 React 钩子,可让你引用渲染不需要的值。
¥useRef
is a React Hook that lets you reference a value that’s not needed for rendering.
const ref = useRef(initialValue)
参考
¥Reference
useRef(initialValue)
在组件的顶层调用 useRef
以声明 引用。
¥Call useRef
at the top level of your component to declare a ref.
import { useRef } from 'react';
function MyComponent() {
const intervalRef = useRef(0);
const inputRef = useRef(null);
// ...
参数
¥Parameters
-
initialValue
:你希望引用对象的current
属性的初始值。它可以是任何类型的值。这个参数在初始渲染后被忽略。¥
initialValue
: The value you want the ref object’scurrent
property to be initially. It can be a value of any type. This argument is ignored after the initial render.
返回
¥Returns
useRef
返回具有单个属性的对象:
¥useRef
returns an object with a single property:
-
current
:最初,它设置为你已通过的initialValue
。你可以稍后将其设置为其他内容。如果将引用对象作为ref
属性传递给 React 给 JSX 节点,React 将设置其current
属性。¥
current
: Initially, it’s set to theinitialValue
you have passed. You can later set it to something else. If you pass the ref object to React as aref
attribute to a JSX node, React will set itscurrent
property.
在接下来的渲染中,useRef
将返回相同的对象。
¥On the next renders, useRef
will return the same object.
注意事项
¥Caveats
-
你可以更改
ref.current
属性。与状态不同,它是可变的。但是,如果它包含一个用于渲染的对象(例如,你的状态的一部分),那么你不应该改变该对象。¥You can mutate the
ref.current
property. Unlike state, it is mutable. However, if it holds an object that is used for rendering (for example, a piece of your state), then you shouldn’t mutate that object. -
当你更改
ref.current
属性时,React 不会重新渲染你的组件。React 不知道你何时更改它,因为引用是一个普通的 JavaScript 对象。¥When you change the
ref.current
property, React does not re-render your component. React is not aware of when you change it because a ref is a plain JavaScript object. -
不要在渲染期间写入或读取
ref.current
,初始化。 除外这会使你的组件的行为不可预测。¥Do not write or read
ref.current
during rendering, except for initialization. This makes your component’s behavior unpredictable. -
在严格模式下,React 将调用你的组件函数两次,以便 帮助你发现意外杂质 这是仅开发行为,不会影响生产。每个引用对象将被创建两次,但其中一个版本将被丢弃。如果你的组件函数是纯函数(它应该是纯函数),这应该不会影响行为。
¥In Strict Mode, React will call your component function twice in order to help you find accidental impurities. This is development-only behavior and does not affect production. Each ref object will be created twice, but one of the versions will be discarded. If your component function is pure (as it should be), this should not affect the behavior.
用法
¥Usage
使用引用引用一个值
¥Referencing a value with a ref
在组件的顶层调用 useRef
以声明一个或多个 引用。
¥Call useRef
at the top level of your component to declare one or more refs.
import { useRef } from 'react';
function Stopwatch() {
const intervalRef = useRef(0);
// ...
useRef
返回一个 引用对象,其中一个 current
属性 初始设置为你提供的 初始值。
¥useRef
returns a ref object with a single current
property initially set to the initial value you provided.
在接下来的渲染中,useRef
将返回相同的对象。你可以更改其 current
属性以存储信息并稍后读取。这可能会让你想起 状态,但有一个重要的区别。
¥On the next renders, useRef
will return the same object. You can change its current
property to store information and read it later. This might remind you of state, but there is an important difference.
更改引用不会触发重新渲染。这意味着引用非常适合存储不影响组件视觉输出的信息。例如,如果你需要存储一个 区间编号 并在以后检索它,你可以将它放在一个引用中。要更新引用中的值,你需要手动更改其 current
属性:
¥Changing a ref does not trigger a re-render. This means refs are perfect for storing information that doesn’t affect the visual output of your component. For example, if you need to store an interval ID and retrieve it later, you can put it in a ref. To update the value inside the ref, you need to manually change its current
property:
function handleStartClick() {
const intervalId = setInterval(() => {
// ...
}, 1000);
intervalRef.current = intervalId;
}
稍后,你可以从引用中读取该间隔 ID,以便你可以调用 清除那个间隔:
¥Later, you can read that interval ID from the ref so that you can call clear that interval:
function handleStopClick() {
const intervalId = intervalRef.current;
clearInterval(intervalId);
}
通过使用引用,你可以确保:
¥By using a ref, you ensure that:
-
你可以在重新渲染之间存储信息(与常规变量不同,常规变量在每次渲染时都会重置)。
¥You can store information between re-renders (unlike regular variables, which reset on every render).
-
更改它不会触发重新渲染(与触发重新渲染的状态变量不同)。
¥Changing it does not trigger a re-render (unlike state variables, which trigger a re-render).
-
该信息对于组件的每个副本来说都是本地的(与外部的变量不同,它们是共享的)。
¥The information is local to each copy of your component (unlike the variables outside, which are shared).
更改引用不会触发重新渲染,因此引用不适合存储要在屏幕上显示的信息。为此使用状态。阅读更多关于 在 useRef
和 useState
之间选择。
¥Changing a ref does not trigger a re-render, so refs are not appropriate for storing information you want to display on the screen. Use state for that instead. Read more about choosing between useRef
and useState
.
例子 1 / 2: 点击计数器
¥Click counter
该组件使用引用来跟踪按钮被点击的次数。请注意,在这里使用引用而不是状态是可以的,因为点击计数仅在事件处理程序中读取和写入。
¥This component uses a ref to keep track of how many times the button was clicked. Note that it’s okay to use a ref instead of state here because the click count is only read and written in an event handler.
import { useRef } from 'react'; export default function Counter() { let ref = useRef(0); function handleClick() { ref.current = ref.current + 1; alert('You clicked ' + ref.current + ' times!'); } return ( <button onClick={handleClick}> Click me! </button> ); }
如果你在 JSX 中显示 {ref.current}
,则该数字不会在点击时更新。这是因为设置 ref.current
不会触发重新渲染。用于渲染的信息应该是状态信息。
¥If you show {ref.current}
in the JSX, the number won’t update on click. This is because setting ref.current
does not trigger a re-render. Information that’s used for rendering should be state instead.
使用引用操作 DOM
¥Manipulating the DOM with a ref
使用引用来操作 DOM. 是特别常见的,React 内置了对此的支持。
¥It’s particularly common to use a ref to manipulate the DOM. React has built-in support for this.
首先,声明一个 引用 object,其 initial value 为 null
:
¥First, declare a ref object with an initial value of null
:
import { useRef } from 'react';
function MyComponent() {
const inputRef = useRef(null);
// ...
然后将你的引用对象作为 ref
属性传递给你要操作的 DOM 节点的 JSX:
¥Then pass your ref object as the ref
attribute to the JSX of the DOM node you want to manipulate:
// ...
return <input ref={inputRef} />;
在 React 创建 DOM 节点并将其放在屏幕上之后,React 会将你的引用对象的 current
属性 设置为该 DOM 节点。现在你可以访问 <input>
的 DOM 节点并像 focus()
一样调用方法:
¥After React creates the DOM node and puts it on the screen, React will set the current
property of your ref object to that DOM node. Now you can access the <input>
’s DOM node and call methods like focus()
:
function handleClick() {
inputRef.current.focus();
}
当节点从屏幕上移除时,React 会将 current
属性设置回 null
。
¥React will set the current
property back to null
when the node is removed from the screen.
阅读更多关于 使用引用操作 DOM。
¥Read more about manipulating the DOM with refs.
例子 1 / 4: 聚焦文本输入
¥Focusing a text input
在此示例中,单击按钮将聚焦输入:
¥In this example, clicking the button will focus the input:
import { useRef } from 'react'; export default function Form() { const inputRef = useRef(null); function handleClick() { inputRef.current.focus(); } return ( <> <input ref={inputRef} /> <button onClick={handleClick}> Focus the input </button> </> ); }
避免重新创建参考内容
¥Avoiding recreating the ref contents
React 会保存一次初始引用值,并在下一次渲染时忽略它。
¥React saves the initial ref value once and ignores it on the next renders.
function Video() {
const playerRef = useRef(new VideoPlayer());
// ...
尽管 new VideoPlayer()
的结果仅用于初始渲染,但你仍会在每次渲染时调用此函数。如果它正在创建昂贵的对象,这可能会很浪费。
¥Although the result of new VideoPlayer()
is only used for the initial render, you’re still calling this function on every render. This can be wasteful if it’s creating expensive objects.
要解决它,你可以像这样初始化引用:
¥To solve it, you may initialize the ref like this instead:
function Video() {
const playerRef = useRef(null);
if (playerRef.current === null) {
playerRef.current = new VideoPlayer();
}
// ...
通常,不允许在渲染期间写入或读取 ref.current
。但是,在这种情况下没问题,因为结果始终相同,并且条件仅在初始化期间执行,因此它是完全可预测的。
¥Normally, writing or reading ref.current
during render is not allowed. However, it’s fine in this case because the result is always the same, and the condition only executes during initialization so it’s fully predictable.
深入研究
¥How to avoid null checks when initializing useRef later
如果你使用类型检查器并且不想总是检查 null
,你可以尝试这样的模式:
¥If you use a type checker and don’t want to always check for null
, you can try a pattern like this instead:
function Video() {
const playerRef = useRef(null);
function getPlayer() {
if (playerRef.current !== null) {
return playerRef.current;
}
const player = new VideoPlayer();
playerRef.current = player;
return player;
}
// ...
在这里,playerRef
本身是可以为空的。但是,你应该能够让你的类型检查器相信 getPlayer()
不会返回 null
。然后在你的事件处理程序中使用 getPlayer()
。
¥Here, the playerRef
itself is nullable. However, you should be able to convince your type checker that there is no case in which getPlayer()
returns null
. Then use getPlayer()
in your event handlers.
故障排除
¥Troubleshooting
我无法获得自定义组件的引用
¥I can’t get a ref to a custom component
如果你尝试像这样将 ref
传递给你自己的组件:
¥If you try to pass a ref
to your own component like this:
const inputRef = useRef(null);
return <MyInput ref={inputRef} />;
你可能会在控制台中收到错误消息:
¥You might get an error in the console:
默认情况下,你自己的组件不会将引用公开给其中的 DOM 节点。
¥By default, your own components don’t expose refs to the DOM nodes inside them.
要解决此问题,请找到你想要获取引用的组件:
¥To fix this, find the component that you want to get a ref to:
export default function MyInput({ value, onChange }) {
return (
<input
value={value}
onChange={onChange}
/>
);
}
然后像这样将它封装在 forwardRef
中:
¥And then wrap it in forwardRef
like this:
import { forwardRef } from 'react';
const MyInput = forwardRef(({ value, onChange }, ref) => {
return (
<input
value={value}
onChange={onChange}
ref={ref}
/>
);
});
export default MyInput;
然后父组件可以获得对它的引用。
¥Then the parent component can get a ref to it.
阅读更多关于 访问另一个组件的 DOM 节点。
¥Read more about accessing another component’s DOM nodes.