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/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/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.htmlReact的refs的理解React的refs的理解