React先進的開發思想一直爲社區所稱道,基於數據流的設計極大地簡化了前端開發成本。但如同官方文檔所述,web開發中有不少場景需求是脫離數據流的,典型的如處理文本輸入框聚焦(focus)。爲此React提供了refs供開發者使用。前端
筆者是在2015年下半年開始學習React,那時候官方文檔關於refs的介紹和使用與如今徹底不一樣。最新的React版本(v15.5.4)已經對這個API進行了修改並更新,不過咱們仍能夠在文檔中找到老版本API的蛛絲馬跡:react
咱們來比較下新老refs有哪些異同:git
在舊版本中,如上圖所述,refs的使用很是簡單,由於每一個組件實例都有一個this.refs
屬性,會自動引用全部包含ref屬性組件的DOM,因此咱們只須要在目標組件上添加一個自定義的ref
,而後進行使用便可:github
class Button extends Component {
constructor(props){
super(props);
}
componentDidMount = () => {
let btn = this.refs.btn;
let link = this.refs.link;
}
render(){
return (
<div>
<button ref='btn'>click</button>
<a href='facebook.github.io/react' ref='link'>click</a>
</div>
)
}
}
複製代碼
文檔加粗部分提醒到,將會在將來的某個版本把這種用法徹底移除掉,建議開發者升級版本後使用新的ref。那麼新的ref如何使用呢?web
直接上代碼:redux
class Input extends Component {
constructor(props){
super(props);
}
focus = () => {
this.textInput.focus();
}
render(){
return (
<div>
<input ref={(input) => { this.textInput = input }} />
</div>
)
}
}
複製代碼
這裏咱們可能就有第一個疑問了,input
參數是哪來的?文檔中這樣解釋:數組
這就說明,當咱們在DOM Element中使用ref
時,回調函數將接收當前的DOM元素做爲參數,而後存儲一個指向這個DOM元素的引用。那麼在示例代碼中,咱們已經把input
元素存儲在了this.textInput
中,在focus
函數中直接使用原生DOM API實現focus聚焦。bash
那麼第二個疑問出現了,回調函數何時被調用?函數
答案是當組件掛載後和卸載後,以及ref屬性自己發生變化時,回調函數就會被調用。單元測試
前面的示例代碼是在DOM添加ref
屬性,那麼咱們來看看如何在組件實例中使用。再上代碼:
//<Input>來源於上面的示例代碼👆
class AutoFocusTextInput extends Component {
componentDidMount(){
this.textInput.focus();
}
render(){
return (
<Input ref={(input) => { this.textInput = input }}>
)
}
}
複製代碼
當咱們在<Input>
中添加ref
屬性時,其回調函數接收已經掛載的組件實例<Input>
做爲參數,並經過this.textInput
訪問到其內部的focus
方法。也就是說,上面的示例代碼實現了當AutoFocusTextInput
組件掛載後<Input>
組件的自動聚焦。
接下來文檔指出,<Input>
組件必須是使用class
聲明的組件,否則沒法使用。這意味着React逐漸與ES6全面接軌了。
緣由很簡單,由於ref
引用的是組件的實例,而無狀態組件準確的說是個函數組件(Functional Component),沒有實例。上代碼:
function MyFunctionalComponent() {
return <input />;
}
class Parent extends React.Component {
render() {
return (
<MyFunctionalComponent
ref={(input) => { this.textInput = input; }} />
);
}
}
複製代碼
上面的代碼是沒法正常工做的。
這是Facebook很是不推薦的作法,由於這樣會打破組件的封裝性,這種方法只是某些特殊場景下的權宜之計。咱們看看如何實現,上代碼:
function CustomTextInput(props) {
return (
<div>
<input ref={props.inputRef} />
</div>
);
}
class Parent extends React.Component {
render() {
return (
<CustomTextInput
inputRef={el => this.inputElement = el}
/>
);
}
}
複製代碼
原理就是父組件把ref
的回調函數當作inputRef
props傳遞給子組件,而後子組件<CustomTextInput>
把這個函數和當前的DOM綁定,最終的結果是父組件<Parent>
的this.inputElement
存儲的DOM是子組件<CustomTextInput>
中的input
。
一樣的道理,若是A組件是B組件的父組件,B組件是C組件的父組件,那麼可用上面的方法,讓A組件拿到C組件的DOM。可是官方態度是discouraged,這種多級調用確實不雅,咱們確實須要考慮其餘更好的方案了。
最近社區對於React的改進建議愈來愈多,例如this.setState()
這樣的回調函數究竟是不是一個好方法,對於複雜程度高,數量多的組件如何高效地進行單元測試,大型應用對於大量state如何進行有效的管理,雖然有redux,mobx這樣優秀的解決方案,但若是react從根本設計上解決這一痛點,是否能再次對前端開發進行新一輪技術革命呢?
今天是2017年5月31日,四年前的5月30日,React正式發佈了。過去的四年是web技術發展最快的四年,無數新技術和新思想噴薄而出。下個四年,咱們共同期待。