在前端開發過程中,必然有些情況會需要dom元素進行一些節點操作,拋開原生的獲取方法,本文將介紹React提供的解決方案,本文所有示例運行版本為react@16.11.0
1. ref
React16.3版本之后React.createRef() API,使用方式如下
class Home extends Component {
constructor(props){
super(props)
this.state = {
}
// ref回調方式獲取
this.pRef = null
this.setCallBackRef = element => {
this.pRef = element
}
this.myNode = React.createRef()
this.myPara = React.createRef()
}
componentDidMount(){
console.log(this.myNode.current)
console.log(this.myPara.current)
// 無current
console.log(this.pRef)
}
render() {
return <div>
<Para ref={this.myPara} />
<p ref={this.setCallBackRef}>123</p>
<p ref={this.myNode}>{this.props.number}</p>
</div>
}
}
當 ref 被傳遞給 render 中的元素時,對該節點的引用可以在 ref 的 current 屬性中被訪問。
ref 的值根據節點的類型而有所不同:
- 當 ref 屬性用于 HTML 元素時,構造函數中使用 React.createRef() 創建的 ref 接收底層 DOM 元素作為其 current 屬性。即:
this.myNode.current
獲取到的元素為dom - 當 ref 屬性用于自定義
class
組件時,ref 對象接收組件的掛載實例作為其 current 屬性。即:this.myPara.current
獲取到的是組件的實例 - 函數組件不能使用ref屬性,因為他們沒有實例
備注:ref在componentDidMount
或 componentDidUpdate
生命周期鉤子觸發前更新。
可以看出,我們在掛在到自定義組件時,獲取到了這個組件的實例,可以獲取到它的props,context以及自定義函數,那么有意思的事情就來了,我們這時可以再父組件中調用子組件的函數來完成某些業務場景
class Para extends React.Component {
close = () => {
alert(1)
}
render(){
return <div>
<p>1</p>
</div>
}
}
class Home extends Component {
constructor(props){
super(props)
this.myPara = React.createRef()
}
render() {
return <div>
<Para ref={this.myPara} />
<button onClick={()=>this.myPara.current.close()}>close</button>
</div>
}
}
運行結果如下這個操作可以實現有趣的功能,可以好好玩一下,特別是在業務組件里。
2. findDOMNode()
reactDom提供了一個api,可以讓我們獲取dom節點,不過大多數情況下不推薦使用,且在嚴格模式下已經被棄用。
棄用原因:findDOMNode 只返回第一個子節點,但是使用 Fragments,組件可以渲染多個 DOM 節點。findDOMNode 是一個只讀一次的 API。調用該方法只會返回第一次查詢的結果。如果子組件渲染了不同的節點,則無法跟蹤此更改。因此,findDOMNode 僅在組件返回單個且不可變的 DOM 節點時才有效。
class Home extends Component {
constructor(props){
super(props)
this.state = {
}
// ref回調方式獲取
this.pRef = null
this.setCallBackRef = element => {
this.pRef = element
}
this.myNode = React.createRef()
this.myPara = React.createRef()
}
// 獲取實例化組件dom
getComponent = () => {
const node = findDOMNode(this.myPara.current)
console.log(node)
}
componentDidMount(){
console.log(this.myNode.current)
console.log(this.myPara.current)
// 無current
console.log(this.pRef)
}
render() {
return <div>
<Para ref={this.myPara} />
<p ref={this.setCallBackRef}>123</p>
<p ref={this.myNode}>{this.props.number}</p>
<button onClick={()=>this.myPara.current.close()}>close</button>
<button onClick={this.getComponent}>getNode</button>
</div>
}
}
ReactDOM下的幾個api都很有趣,回頭分析一下,埋個坑。
3.useRef
react@16.8版本之后,使用hook,hook提供了useRef來獲取dom,注意:返回的 ref 對象在組件的整個生命周期內保持不變(current是變化的),相對React.createRef()在生命周期過程中是一直變化的。代碼如下
import React, { useRef, useEffect } from 'react';
import Para from './Para'
const UseRef = () => {
const myNode = useRef(null);
const myPara = useRef(null);
useEffect(() => {
console.log(myNode.current)
console.log(myPara.current)
}, []);
return <div>
<p ref={myNode}>123</p>
<Para ref={myPara} />
</div>
}
export default UseRef;