findDOMNode
方法可以获取 类式组件 实例对应的浏览器 DOM 节点。
const domNode = findDOMNode(componentInstance)
参考
findDOMNode(componentInstance)
使用 findDOMNode
获取 类式组件 实例对应的浏览器 DOM 节点。
import { findDOMNode } from 'react-dom';
const domNode = findDOMNode(componentInstance);
参数
componentInstance
:Component
子类的实例。举个例子,类式组件中的this
。
返回值
findDOMNode
方法返回与给定的 componentInstance
中最接近的浏览器 DOM 节点。当组件渲染为 null
或 false
时,findDOMNode
返回 null
。当组件渲染为字符串时,findDOMNode
返回一个包含该值的文本 DOM 节点。
注意
-
组件可能会返回包含多个子元素的数组或 Fragment。在这种情况下,
findDOMNode
会返回第一个非空子节点对应的 DOM 节点。 -
findDOMNode
只对已经挂载到 DOM 上的组件有效。如果你尝试在一个还未挂载的组件上调用findDOMNode()
(比如在一个还未创建的组件的render()
方法中调用findDOMNode()
),会抛出异常。 -
findDOMNode
只会返回调用时的结果,你无法得知组件是否在之后渲染了不同的节点。 -
findDOMNode
接受类组件实例作为参数,而不能用于函数式组件。
用法
寻找类式组件对应的 DOM 节点
使用 findDOMNode
获取 类式组件 实例(通常是 this
)对应的已渲染 DOM 节点。
class AutoselectingInput extends Component {
componentDidMount() {
const input = findDOMNode(this);
input.select()
}
render() {
return <input defaultValue="你好" />
}
}
在这里,input
变量将被设置为 <input>
DOM 元素,这样你就可以对其进行操作。例如,当点击下方的“显示示例”按钮并挂载了输入框后,input.select()
会选中输入框中的所有文本:
import { Component } from 'react'; import { findDOMNode } from 'react-dom'; class AutoselectingInput extends Component { componentDidMount() { const input = findDOMNode(this); input.select() } render() { return <input defaultValue="你好" /> } } export default AutoselectingInput;
替代方案
使用 ref 读取组件自己的 DOM 节点
由于 JSX 节点与操作相应的 DOM 节点的代码之间的联系不是显式的,因此使用 findDOMNode
的代码非常脆弱。例如,尝试将此 <input />
包装在一个 <div>
中:
import { Component } from 'react'; import { findDOMNode } from 'react-dom'; class AutoselectingInput extends Component { componentDidMount() { const input = findDOMNode(this); input.select() } render() { return <input defaultValue="Hello" /> } } export default AutoselectingInput;
这将出现问题。findDOMNode(this)
找到的是 <div>
节点,但其实我们期望找到的是 <input>
节点。为了避免这些问题,考虑使用 createRef
管理特定的 DOM 节点。
在这个例子中不再使用 findDOMNode
。相反,使用 inputRef = createRef(null)
并将其定义为类的实例字段。如果想要从中读取 DOM 节点,可以使用this.inputRef.current
。如果想要将其附加在 JSX 上,考虑渲染 <input ref={this.inputRef} />
。这将连接使用 DOM 节点的代码与对应 JSX。
import { createRef, Component } from 'react'; class AutoselectingInput extends Component { inputRef = createRef(null); componentDidMount() { const input = this.inputRef.current; input.select() } render() { return ( <input ref={this.inputRef} defaultValue="你好" /> ); } } export default AutoselectingInput;
在函数式组件中,你应该使用 useRef
:
import { useRef, useEffect } from 'react'; export default function AutoselectingInput() { const inputRef = useRef(null); useEffect(() => { const input = inputRef.current; input.select(); }, []); return <input ref={inputRef} defaultValue="你好" /> }
如果你想了解更多,请阅读 使用 ref 操作 DOM。
使用 ref 操作子组件的 DOM 节点
在这个例子中,findDOMNode(this)
获取了属于另一个组件的 DOM 节点。AutoselectingInput
渲染了我们自己的 MyInput
组件,而 MyInput
渲染了浏览器标签 <input>
。
import { Component } from 'react'; import { findDOMNode } from 'react-dom'; import MyInput from './MyInput.js'; class AutoselectingInput extends Component { componentDidMount() { const input = findDOMNode(this); input.select() } render() { return <MyInput />; } } export default AutoselectingInput;
请注意,即使 <input>
被隐藏在 MyInput
组件中,在 AutoselectingInput
中调用 findDOMNode(this)
仍然会返回 <input>
。这在上面的例子中似乎很方便,但它会导致代码变得非常脆弱。想象一下,如果你以后想编辑 MyInput
并使用 <div>
包装,但是这将会破坏 AutoselectingInput
的代码(因为它期望找到 <input>
)。
考虑在这个例子中替换 findDOMNode
,并且下列两个组件需要协同工作:
- 在
AutoSelectingInput
中声明一个 ref,就像 前面的例子 中一样,并将其传递给<MyInput>
。 MyInput
使用forwardRef
接收该 ref 并将其转发到<input>
节点。
这种方式解决了这个问题,所以不再需要 findDOMNode
:
import { createRef, Component } from 'react'; import MyInput from './MyInput.js'; class AutoselectingInput extends Component { inputRef = createRef(null); componentDidMount() { const input = this.inputRef.current; input.select() } render() { return ( <MyInput ref={this.inputRef} /> ); } } export default AutoselectingInput;
以下是上面代码在函数式组件中的样子:
import { useRef, useEffect } from 'react'; import MyInput from './MyInput.js'; export default function AutoselectingInput() { const inputRef = useRef(null); useEffect(() => { const input = inputRef.current; input.select(); }, []); return <MyInput ref={inputRef} defaultValue="你好" /> }
使用 <div>
包装
有时,一个组件想要知道子元素的位置和大小。这会让你想要使用 findDOMNode(this)
查找子元素,然后使用 getBoundingClientRect
等 DOM 方法来进行测量。
目前,还没有直接适用于此场景的替代方法,这就是为什么 findDOMNode
已弃用但尚未从 React 中完全删除的原因。在此期间,你可以尝试在内容周围使用 <div>
包装,并向其添加 ref。但是,额外的包装可能会破坏样式。
<div ref={someRef}>
{children}
</div>
这对于聚焦以及滚动到任何子元素也是一样。