这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战
翻译自:beta.reactjs.org/learn/manip…
因为 React 已经根据 render 的输出处理了 DOM 结构,所以你的组件不经常需要操作 DOM。然而,有的时候你可能需要操作 React 管理的 DOM 元素,比如,将焦点放到一个节点上,滚动到这个节点,或者去计算它的宽和高。React 中没有内置的方法去做这些事情,所以你将会需要 ref 去指向这个 DOM 节点。
这个系列的文章你将会学到:
- 如何使用 ref 属性访问由 React 管理的 DOM 节点
- 如何将 JSX 的
ref
属性关联到useRef
钩子 - 如何访问其他组件的 DOM 节点
- 在哪种情况下,修改 React 管理的 DOM 是安全的
关于 ref 相关的介绍和例子,可以看我前面一个系列的文章 useRef 简单易懂解析
系列文章
如何使用 ref 回调去管理列表的 ref
在上面的例子中,有预定数量的 ref,但是有时你可能列表的每一项都需要有 ref,不知道列表的数目,但是按照下面做是行不通的:
<ul>
{items.map((item) => {
// Doesn't work!
const ref = useRef(null);
return <li ref={ref} />;
})}
</ul>
上面这样写是不行的,因为 Hooks 只能在组件的顶层调用。不能在循环、条件、map 中调用 useRef
。
相反,解决方案是将函数传递给 ref
属性,这称为“ref 回调”,当设置 ref 的时候,React 将使用 DOM 节点调用您的 ref 回调,并在需要清除它时使用 null。这使你可以维护自己的数组或 Map,并通过其 index 或某种 ID 访问任何 ref。
此示例显示如何使用此方法滚动到长列表中的任意节点:
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) {
// Initialize the Map on first usage.
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 节点的映射。(Refs 可以保存任何值!)每个列表项上的 ref
回调会注意更新 Map:
<li
key={cat.id}
ref={node => {
const map = getMap();
if (node) {
// Add to the Map
map.set(cat.id, node);
} else {
// Remove from the Map
map.delete(cat.id);
}
}}
>
这使您可以稍后从 Map 读取单个 DOM 节点。