我们在一些 React 项目中,经常会有一些类库,需要传入 DOM 元素或者该元素的选择器。那么在多次调用组件时,如何区分不同的 div 容器呢?
1. 从外部传入标识 #
我们在每次调用时,都从外部传入一个标识,通过这个标识来区分不同的 div 容器。
// 自定义组件
const MyComponent = ({ id }) => {
useEffect(() => {
const dom = document.getElementById(id);
console.log(dom);
}, []);
return <div id={id}></div>;
};
// 调用时
const App = () => {
return (
<div>
<MyComponent id="id-1" />
<MyComponent id="id-2" />
<MyComponent id="id-3" />
</div>
);
};
这种方式的好处是元素的标识是外部传入的,不会因为多次调用组件而改变,同时可控,当其他组件也需要到该 DOM 元素时,也能准确获取到。
但不太好的点是,传入的标识,需要开发者自己确保唯一性,否则会产生冲突。
2. 通过 useRef 获取 #
在 React 中,可以使用 useRef()
的 hook 来获取组件的 DOM 元素。
// 自定义组件
const MyComponent = () => {
const domRef = useRef(null);
useEffect(() => {
// 当前指定的dom元素
console.log(domRef.current);
}, []);
return <div ref={domRef}></div>;
};
// 调用时
const App = () => {
return (
<div>
<MyComponent />
<MyComponent />
<MyComponent />
</div>
);
};
这种方式,useRef()
在组件多次调用时,都是互相隔离的,获取的只是当前组件中的 DOM 元素。感兴趣地可以看下文章React 中 useState 和 useRef 与全局变量的区别。
3. 自增 id #
我们也可以通过自增的方式,生成一个唯一的 id。利用 useState()
只能通过 setState()
修改数据的特性,可以在初始化时,生成一个唯一的 id。
let globalIndex = 0;
const MyComponent = () => {
/**
* 多次调用当前组件时,globalIndex会自增,并且只会在组件初始化时执行,
* 此后组件再更新时,id是不会变的
**/
const [id] = useState(`id-${globalIndex++}`);
useEffect(() => {
console.log(id, document.getElementById(id));
}, []);
return <div id={id}></div>;
};
// 调用时
const App = () => {
return (
<div>
<MyComponent />
<MyComponent />
<MyComponent />
</div>
);
};
输出结果:
id-0 <div id="id-0"></div>
id-1 <div id="id-1"></div>
id-2 <div id="id-2"></div>
同样地,这里的唯一 id,除了使用全局的自增计数器外,也可以使用其它的方式,如:uuid
、nanoid
等。
4. 通过 useId 设置唯一 id #
React 16.14 版本中,新增了 useId()
hook,用于生成一个唯一的 id。当组件多次调用时,生成的 id 也是互不冲突的。
// 使用 useId() 生成唯一id
const MyComponent = () => {
const id = useId();
return <div id={id}></div>;
};
// 调用时
const App = () => {
return (
<div>
<MyComponent />
<MyComponent />
<MyComponent />
</div>
);
};
最终生成的 dom 元素是:
<div>
<div id=":r0:"></div>
<div id=":r1:"></div>
<div id=":r2:"></div>
</div>
如果同一个组件中,需要生成多个不同的 id,可以多次调用 useId()
。
如果是在服务端渲染的场景中,最好是使用useId()
的方案,React 能够保证在服务端和客户端生成的 id 是相同的。
若使用自增计数器的方式,随着 React Fizz(React 新的服务端流式渲染器)的到来,服务端渲染顺序和客户端挂载的顺序可能不再一致。导致服务端和客户端产生的 id 不一样。
若是使用时间戳或随机数等方式,更不可能在服务端和客户端生成相同的 id 了。
5. 总结 #
能够获取到当前组件的 DOM 元素,是 React 中一个非常常用的需求。大家可以根据不同的场景来使用不同的方式。