使用refs获取节点_闲庭信步聊前端 - 原来你是这样的Refs
一、refs 的由來
什么是refs
refs是拿到真實的DOM節點和React元素實例的一種方法。在React官方文檔中有提到Refs 提供了一種方式,允許我們訪問 DOM 節點或在 render 方法中創建的 React 元素。 React是單向的數據流,父子組件的交互是通過props。修改子組件需要使用新的props來重新渲染子組件。但是在某些情況下,你需要修改數據流以外的子組件,如DOM元素或者一個React元素實例,此時就需要Refs來改變。
refs的適用場景
根據官方文檔,refs在以下幾種場景中適用: 1. 操作DOM元素、控制文本內容或者媒體播放 2. 操作DOM元素,觸發強制動畫 3. 集成第三方DOM庫我們來看下,React中有以下四種使用方式
二、refs的四種方式
1. callback ref
React 支持一種通過回調的方式設置refs,這種方式可以控制refs何時被設置和解除。創建ref屬性時,你會傳遞一個函數,這個函數會接受DOM元素或者組件實例作為參數,使他們能夠在其他地方被訪問。
class TestTemp extends React.Component {componentDidMount() {this.myRef.focus();}render() {return <input ref={(element) => {this.myRef = element;}} />;} }React在componetDidMount或者componetDidUpdate觸發前調用ref回調,并且傳入對應的DOM元素,保證refs是最新的。 在下面的例子中,父組件可以獲取到子組件的DOM節點
function Child(props) {return (<div><input ref={props.myRef} /></div>); } class Parent extends React.Component {componentDidMount() {this.myElement.focus();}render() {return (<ChildmyRef={element => this.myElement = element}/>);} }父組件通過props傳一個回調函數給子組件,子組件可以把這個函數作為ref屬性的值綁定到DOM元素上。this.myElement就是對應子組件的DOM元素(input)
2. React.createRef()
createRef()是通過創建一個ref屬性,傳遞給渲染的DOM元素或者是組件實例
class TestTemp extends React.Component {constructor(props) {super(props);this.myRef = React.createRef();}componentDidMount() {this.myRef.current.focus()}render() {return (<input ref={ this.myRef }></input>)} }在上面的例子中,創建一個ref屬性實例myRef,并將其傳給DOM元素 input中。之后對此元素的操作就可以在ref實例的current屬性中處理。 對元素的操作根據節點類型的不同,ref的值也不同: 1. 當ref屬性作用于一個HTML元素時,createRef()創建ref實例的current實例是底層DOM元素
class TestTemp extends React.Component {constructor(props) {super(props);// 創建一個ref存儲 input元素this.myRef = React.createRef();}componentDidMount() {// 獲取 input元素焦點this.myRef.current.focus()}render() {return (<div><input ref={this.myRef}></input></div>)} }2. 當ref屬性作用于一個自定義組件時,createRef()創建ref實例的current實例是組件的掛載實例
class Children extends React.Component {constructor(props) {super(props);// 創建一個ref實例,存儲input元素this.childRef = React.createRef();}childInput() {// 獲取input焦點this.childRef.current.focus();}render(){return(<div><input type='text' ref={ this.childRef }/></div>)} } class Parent extends React.Component {constructor(props) {super(props);// 創建ref實例 存儲Children 組件實例this.parentRef = React.createRef();}componentDidMount() {// 通過ref調用Children 組件的childInput方法,獲取子組件input的焦點this.parentRef.current.childInput();}render() {return (<Children ref={ this.parentRef } />);} }3. ref屬性不可在函數組件上使用,因為函數組件沒有實例。
3. useRef()
useRef 是hooks家族中的一員,他在react hook中的作用,像是一個變量,類似如this,它可以存放任何的東西。createRef每次都會創建一個新的實例,而useRef每次都會返回相同的引用,返回的ref對象在組件的整個生命周期內保持不變。 因為特性的不同,createRef和useRef也有很大的不同
function TestTemp() {const [renderIndex, setRenderIndex] = useState(1);const refFromUseRef = useRef();const refFromCreateRef = createRef();if (!refFromUseRef.current) {refFromUseRef.current = renderIndex;}if (!refFromCreateRef.current) {refFromCreateRef.current = renderIndex;}return (<div>index: {renderIndex}<br />在refFromUseRef:{refFromUseRef.current}<br />在refFromCreateRef:{refFromCreateRef.current}<br /><button onClick={() => setRenderIndex(prev => prev + 1)}>增加</button></div>); }執行上面的代碼,會發現index和refFromCreateRef是一直在隨著點擊增加的,而refFromUseRef則是保持不變。 就算組件重新渲染,由于refFromUseRef的值一直存在,類似于this,無法重新賦值,因此結果不會變。當ref對象內容發生變化時,useRef并不會通知你,更改current屬性也不會導致重新渲染,因為它一直時一個引用。
何時適合用useRef
那么為什么要有useRef這個API呢,我們來看一下下面的例子
function TestTemp() {const [count, setCount] = useState(0);function handleAlertclick() {setTimeout(() => {alert("count:" + count);}, 2000);}return (<div><p>當前count: {count} </p><button onClick={() => setCount(count + 1)}>增加</button><button onClick={handleAlertclick}> 彈窗</button></div>) }按照如下步驟執行操作: 1. 點擊兩次增加按鈕后,點擊彈窗按鈕 2. 在彈窗未展示之前,迅速再次點擊兩次增加按鈕,等待彈窗出現
執行操作后發現,彈窗顯示的結果是"count: 2",頁面上的count值為4。彈窗顯示的只是當時點擊時的快照。
為什么彈窗中不是最新的count呢?
當我們更新狀態時,React會重新渲染組件,每一次渲染都會拿到當前的count值,并重新渲染點擊事件(handleAlertclick函數),因此每個函數里面都有自己的count。這樣我們就理解了上面的例子中,彈窗中就是點擊時的count值。
如何彈出時獲取到實時的值呢?
function TestTemp() {const [count, setCount] = useState(0);const useRefCount = useRef(count);useEffect(() => {useRefCount.current = count;});function handleAlertclick() {setTimeout(() => {alert("useRefCount.current:" + useRefCount.current + "count:" + count);}, 2000);}return (<div><p>當前count: {count} </p><button onClick={() => setCount(count + 1)}>增加</button><button onClick={handleAlertclick}>彈窗</button></div>) }執行同上個例子中的操作,結果彈窗中顯示"useRefCount.current: 4 count: 2"。使用 useRef 能獲取到最新的值,但是 useState 卻不能。 因為useRef每次都會返回同一個引用,因此在useEffect中修改時,彈窗中的也會被修改。
4. 過時API:string ref
class TestTemp extends React.Component {componentDidMount() {this.refs.myRef.focus();}render() {return <input ref="myRef" />;} }string ref是通過this.refs.myRef來訪問DOM節點。但是現在不建議以這種方式創建ref屬性,它已過時并可能在未來的版本被移除。React官方文檔中有提出
如果你目前還在使用 this.refs.textInput 這種方式訪問 refs ,我們建議用回調函數或 createRef API 的方式代替。三、Refs轉發
通常我們不需要在父組件中引用子組件中的DOM節點,但是在一些特殊情況下,避免不了這種需求,比如一些可重用的組件。因此我們可以使用ref轉發,ref轉發使組件可以像暴露自己的ref一樣暴露子組件的ref。
Ref 轉發是一個可選特性,其允許某些組件接收 ref,并將其向下傳遞(換句話說,“轉發”它)給子組件。 如果你使用16.3或者更高版本的React可以使用React.forwardRef來獲取傳遞的ref,并且傳遞給需要引用的DOM元素// React.forwardRef()接收ref作為第二個參數 // 子組件接收此ref并且傳遞給DOM元素 const Child = React.forwardRef((props, ref) => (<button ref={ref}>{props.children}</button> )); // 父組件創建一個ref傳遞給子組件 // 通過myRef.current就可以引用button元素 const myRef = React.createRef(); <Child ref={myRef}>點擊</Child>如果你使用的是低版本的React,可以通過props傳遞ref來模擬React.forwardRef()函數
function Child(props) {return (<div><input ref={props.childRef} /></div>); } class Parent extends React.Component {constructor(props) {super(props);this.myRef = React.createRef();}componentDidMount() {this.myRef.current.focus();}render() {return (<Child childRef={this.myRef} />);} } 注意:ref只在React.forwardRef()函數中作為第二參數存在,在常規的函數和Class組件是不接收ref參數的四、Refs 傳遞原理
React中,HostComponent、ClassComponent、ForwardRef可以賦值ref屬性。ForwardRef只是將ref作為第二個參數傳遞下去,沒有別的特殊處理。 Ref屬性在ref不同的生命周期會被執行 ( fuction類型 ) 或賦值 ( {current: any}對象類型 ) ref的生命周期與react的渲染一樣,可以分為兩個階段
render階段:
為含有ref屬性的component對應fiber添加Ref effectTag, fiber類型為HostComponent、ClassComponent、ScopeComponent
commit階段:
當給HTML元素添加ref屬性時,ref回調接收了底層的DOM元素作為參數。在源碼中有以下兩個方法 1. commitAttachRef 掛載實例,當組件渲染完成后,在componentDidMount/componentDidUpdate后被執行。finishedWork為含有Ref effectTag的fiber 掛載時將DOM元素傳入ref的回調函數
2. commitDetachRef 移除實例,在組件或元素被銷毀前執行,在componentWillUnmount之前清理引用。 卸載時傳入null,將當前的DOM元素賦值為null
總結
以上是生活随笔為你收集整理的使用refs获取节点_闲庭信步聊前端 - 原来你是这样的Refs的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (美邑备案)
- 下一篇: ai画笔画的图怎么上色(ai画笔画的图怎