这是我参与11月更文挑战的第15天,活动详情查看: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 操作 DOM?(一)使用 ref 访问 DOM 节点
- 如何使用 ref 操作 DOM?(二)使用示例
- 如何使用 ref 操作 DOM?(三)如何使用 ref 回调去管理列表的 ref
访问另一个组件的 DOM 节点
当你在内置组件(会被输出为浏览器的元素比如 <input />
)上设置 ref
的时候,React 会将这个 ref 的 current 属性设置为相应的 DOM 节点(比如浏览器中实际的 <input />
)。
但是,如果你尝试将 ref 放在你自己的组件上,比如 <MyInput />
,默认情况下,您将获得 null
。这是一个演示它的示例,请注意单击按钮后不会聚焦到 input。
import { useRef } from 'react';
function MyInput(props) {
return <input {...props} />;
}
export default function MyForm() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>
<MyInput ref={inputRef} />
<button onClick={handleClick}>
Focus the input
</button>
</>
);
}
单击该按钮将打印一个错误消息到控制台:
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
警告:不能为函数组件提供引用。尝试访问此引用将失败。你的意思是使用 React.forwardRef() 吗?
发生这种情况是因为默认情况下 React 不允许组件访问其他组件的 DOM 节点。甚至自己的 children 也不行!这是故意的这样设计的。Refs 是一个逃生舱,应该谨慎使用。手动操作另一个组件的 DOM 节点会使您的代码更加脆弱。
相反,想要公开其 DOM 节点的组件必须选择加入这个行为。一个组件可以指定它将它的引用“转发”给它的一个子组件。以下是 MyInput
如何使用 forwardRef
API:
const MyInput = forwardRef((props, ref) => {
return <input {...props} ref={ref} />;
});
这是它的工作原理:
<MyInput ref={inputRef} />
告诉 React 将对应的 DOM 节点放入inputRef.current
中。但是,由 MyInput 组件来选择是否加入,默认情况下,它不会加入。MyInput
组件是使用forwardRef
声明的,这让它从上一层级接收 inputRef 这个参数,作为在 props 之后声明的第二个 ref 参数。MyInput
本身将它接收到的ref传递给它内部的<input>
。
这样,点击按钮来聚焦 input 就可以实现了:
import { forwardRef, useRef } from 'react';
const MyInput = forwardRef((props, ref) => {
return <input {...props} ref={ref} />;
});
export default function Form() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>
<MyInput ref={inputRef} />
<button onClick={handleClick}>
Focus the input
</button>
</>
);
}
在设计系统中,按钮、input 等低级组件将其引用转发到它们的 DOM 节点是一种常见的模式。另一方面,像表单、列表或页面这样的高级组件通常不会公开它们的 DOM 节点,以避免依赖于 DOM 结构。