1. 程式人生 > 其它 >React中refs的理解

React中refs的理解

技術標籤:reactjavascript

React中refs的理解

Refs提供了一種方式,允許我們訪問DOM節點或在render方法中建立的React元素。

描述

在典型的React資料流中,props是父元件與子元件互動的唯一方式,要修改一個子元件,你需要使用新的props來重新渲染它,但是在某些情況下,你需要在典型資料流之外強制修改子元件,被修改的子元件可能是一個React元件的例項,也可能是一個DOM元素,對於這兩種情況React都提供瞭解決辦法。
避免使用refs來做任何可以通過宣告式實現來完成的事情,通常在可以使用propsstate的情況下勿依賴refs,下面是幾個適合使用refs

的情況:

  • 管理焦點、文字選擇或媒體播放。
  • 觸發強制動畫。
  • 整合第三方DOM庫。

使用

React提供的這個ref屬性,表示為對元件真正例項的引用,其實就是ReactDOM.render()返回的元件例項,需要區分一下渲染元件與渲染原生DOM元素,渲染元件時返回的是元件例項,而渲染DOM元素時,返回是具體的DOM節點,Reactref3種用法。

字串

ref可以直接設定為字串值,這種方式基本不推薦使用,或者在未來的React版本中不會再支援該方式。這主要是因為使用字串導致的一些問題,例如當ref定義為string時,需要React追蹤當前正在渲染的元件,在reconciliation階段,React Element

建立和更新的過程中,ref會被封裝為一個閉包函式,等待commit階段被執行,這會對React的效能產生一些影響等。

class InputOne extends React.Component {
    componentDidMount() {
        this.refs.inputRef.value = 1;
      }
    render() {
        return <input ref="inputRef" />;
    }
}

回撥

React支援給任意元件新增特殊屬性,ref屬性接受一個回撥函式,其在元件被載入或解除安裝時會立即執行。

  • 當給HTML元素新增ref屬性時,ref回撥接收了底層的DOM元素作為引數。
  • 當給元件新增ref屬性時,ref回撥接收當前元件例項作為引數。
  • 當元件解除安裝的時候,會傳入null
  • ref回撥會在componentDidMountcomponentDidUpdate等生命週期回撥之前執行。

Callback Ref我們通常會使用行內函數的形式,那麼每次渲染都會重新建立,由於React會清理舊的ref然後設定新的,因此更新期間會呼叫兩次,第一次為null,如果在Callback中帶有業務邏輯的話,可能會出錯,可以通過將Callback定義成類成員函式並進行繫結的方式避免。

class InputTwo extends React.Component {
    componentDidMount() {
        this.inputRef.value = 2;
      }
    render() {
        return <input ref={(element) =>this.inputRef = element} />;
    }
}

API建立

React v16.3中經0017-new-create-ref提案引入了新的React.createRefAPI,當ref被傳遞給render中的元素時,對該節點的引用可以在refcurrent屬性中被訪問,ref的值根據節點的型別而有所不同:

  • ref屬性用於HTML元素時,建構函式中使用React.createRef()建立的ref接收底層DOM元素作為其current屬性。
  • ref屬性用於自定義class元件時,ref物件接收元件的掛載例項作為其current屬性。
  • 不能在函式元件上使用ref屬性,因為他們沒有例項。

對比新的CreateRefCallback Ref,並沒有壓倒性的優勢,只是希望成為一個便捷的特性,在效能上會會有微小的優勢,Callback Ref採用了元件Render過程中在閉包函式中分配ref的模式,而CreateRef則採用了Object Ref

class InputThree extends React.Component {
    constructor(props) {
        super(props);
        this.inputRef = React.createRef();
    }
    componentDidMount() {
        this.inputRef.current.value = 3;
    }
    render() {
        return <input ref={this.inputRef} />;
    }
}

示例

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8" />
    <title>React</title>
</head>

<body>
    <div id="root"></div>
</body>
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
    class InputOne extends React.Component {
        componentDidMount() {
            this.refs.inputRef.value = 1;
          }
        render() {
            return <input ref="inputRef" />;
        }
    }

    class InputTwo extends React.Component {
        componentDidMount() {
            this.inputRef.value = 2;
          }
        render() {
            return <input ref={(element) =>this.inputRef = element} />;
        }
    }

    class InputThree extends React.Component {
        constructor(props) {
            super(props);
            this.inputRef = React.createRef();
        }
        componentDidMount() {
            this.inputRef.current.value = 3;
        }
        render() {
            return <input ref={this.inputRef} />;
        }
    }
    

    var vm = ReactDOM.render(
        <>
            <InputOne />
            <InputTwo />
            <InputThree />
        </>,
        document.getElementById("root")
    );
</script>

</html>

每日一題

https://github.com/WindrunnerMax/EveryDay

參考

https://zhuanlan.zhihu.com/p/40462264
https://www.jianshu.com/p/4e2357ea1ba1
https://juejin.cn/post/6844903809274085389
https://juejin.cn/post/6844904048882106375
https://segmentfault.com/a/1190000008665915
https://zh-hans.reactjs.org/docs/refs-and-the-dom.html