如何使用 ref 操作 DOM?(三)如何使用 ref 回调去管理列表的 ref

·  阅读 1038

这是我参与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 节点。

分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改