先看下来自官方的介绍,当你希望组件"记住"某些信息,但又不想让这些信息 触发新的渲染 时,你可以使用 ref 。
- 如何向组件添加 ref
- 如何更新 ref 的值
- ref 与 state 有何不同
- 如何安全地使用 ref
1.给你的组件添加 ref
你可以通过从 React 导入 useRef
Hook 来为你的组件添加一个 ref:
import { useRef } from 'react';
export default function Counter() {
let ref = useRef(0);
function handleClick() {
ref.current = ref.current + 1;
alert('你点击了 ' + ref.current + ' 次!');
}
return (
<button onClick={handleClick}>
点击我!
{ref.current}
</button>
);
}
在你的组件内,调用 useRef
Hook 并传入你想要引用的初始值作为唯一参数。这里的 ref 引用的值是"0",点击后也不变。
以下是 state 和 ref 的对比:
ref | state |
---|---|
useRef(initialValue) 返回 { current: initialValue } |
useState(initialValue) 返回 state 变量的当前值和一个 state 设置函数 ( [value, setValue] ) |
更改时不会触发重新渲染 | 更改时触发重新渲染。 |
可变 ------ 你可以在渲染过程之外修改和更新 current 的值。 |
"不可变" ------ 你必须使用 state 设置函数来修改 state 变量,从而排队重新渲染。 |
你不应在渲染期间读取(或写入) current 值。 |
你可以随时读取 state。但是,每次渲染都有自己不变的 state 快照。 |
2.使用 ref 操作 DOM
由于 React 会自动处理更新 DOM 以匹配你的渲染输出,因此你在组件中通常不需要操作 DOM。但是,有时你可能需要访问由 React 管理的 DOM 元素 ------ 例如,让一个节点获得焦点、滚动到它或测量它的尺寸和位置。在 React 中没有内置的方法来做这些事情,所以你需要一个指向 DOM 节点的 ref 来实现。
示例: 使文本输入框获得焦点
在本例中,单击按钮将使输入框获得焦点:
import { useRef } from 'react';
export default function Form() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>
<input ref={inputRef} />
<button onClick={handleClick}>
聚焦输入框
</button>
</>
);
}
如何使用 ref 回调管理 ref 列表
-
这里使用map搭配useRef作存储
import { useRef } from 'react';
export default function CatFriends() {
const itemsRef = useRef(null);function scrollToId(itemId) { const map = getMap(); const node = map.get(itemId); node.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }); } function getMap() { if (!itemsRef.current) { // 首次运行时初始化 Map。 itemsRef.current = new Map(); } return itemsRef.current; } return ( <> <nav> <button onClick={() => scrollToId(0)}> Tom </button> <button onClick={() => scrollToId(5)}> Maru </button> <button onClick={() => scrollToId(9)}> Jellylorum </button> </nav> <div> <ul> {catList.map(cat => ( <li key={cat.id} ref={(node) => { const map = getMap(); if (node) { map.set(cat.id, node); } else { map.delete(cat.id); } }} > <img src={cat.imageUrl} alt={'Cat #' + cat.id} /> </li> ))} </ul> </div> </> );
}
const catList = [];
for (let i = 0; i < 10; i++) {
catList.push({
id: i,
imageUrl: 'https://placekitten.com/250/200?image=' + i
});
}
itemsRef
保存的不是单个 DOM 节点,而是保存了包含列表项 ID 和 DOM 节点的 Map。(Ref 可以保存任何值!) 每个列表项上的 ref 回调负责更新 Map。
3.访问另一个组件的 DOM 节点
当你将 ref 放在像 <input />
这样输出浏览器元素的内置组件上时,React 会将该 ref 的 current
属性设置为相应的 DOM 节点(例如浏览器中实际的 <input />
)。
但是,如果你尝试将 ref 放在 你自己的 组件上,例如 <MyInput />
,默认情况下你会得到 null
。这个示例演示了这种情况。请注意单击按钮 并不会 聚焦输入框: