React中refs的理解
技術標籤:reactjavascript
React中refs的理解
Refs
提供了一種方式,允許我們訪問DOM
節點或在render
方法中建立的React
元素。
描述
在典型的React
資料流中,props
是父元件與子元件互動的唯一方式,要修改一個子元件,你需要使用新的props
來重新渲染它,但是在某些情況下,你需要在典型資料流之外強制修改子元件,被修改的子元件可能是一個React
元件的例項,也可能是一個DOM
元素,對於這兩種情況React
都提供瞭解決辦法。
避免使用refs
來做任何可以通過宣告式實現來完成的事情,通常在可以使用props
與state
的情況下勿依賴refs
,下面是幾個適合使用refs
- 管理焦點、文字選擇或媒體播放。
- 觸發強制動畫。
- 整合第三方
DOM
庫。
使用
React
提供的這個ref
屬性,表示為對元件真正例項的引用,其實就是ReactDOM.render()
返回的元件例項,需要區分一下渲染元件與渲染原生DOM
元素,渲染元件時返回的是元件例項,而渲染DOM
元素時,返回是具體的DOM
節點,React
的ref
有3
種用法。
字串
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
回撥會在componentDidMount
或componentDidUpdate
等生命週期回撥之前執行。
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.createRef
的API
,當ref
被傳遞給render
中的元素時,對該節點的引用可以在ref
的current
屬性中被訪問,ref
的值根據節點的型別而有所不同:
- 當
ref
屬性用於HTML
元素時,建構函式中使用React.createRef()
建立的ref
接收底層DOM
元素作為其current
屬性。 - 當
ref
屬性用於自定義class
元件時,ref
物件接收元件的掛載例項作為其current
屬性。 - 不能在函式元件上使用
ref
屬性,因為他們沒有例項。
對比新的CreateRef
與Callback 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