React Refs
有些情況下,你會需要直接存取和操作 DOM 元素 (element),像是要 focus 表單欄位,React 提供了 ref
(Reference) 屬性 (attribute) 來處理這個需求。
ref
,以下會分別說明。從 React v16.3 開始 - createRef()
我們可以用 React.createRef()
來建立 ref
在 React v16.3 之前的版本 - callback
ref 需要設定一個 callback function,這個 function 會在元件被掛載 (mount) 及卸載 (unmount) 時被執行,當這個 callback function 執行時會傳入 DOM 元素的參照 (reference),這時你可以將這個 reference 先存起來,稍後就可以使用它來存取 DOM 元素囉。
ref
可以設在 HTML element,也可以設在 Component 上:
DOM Element
如果
ref
是設在 HTML 元素上面,ref function 被執行時,會傳入一個參數,表示實際的 DOM element,你可以將這個 reference 存下來供後續使用。例子:
class CustomTextInput extends React.Component { constructor(props) { super(props); this.focusTextInput = this.focusTextInput.bind(this); } // 按鈕 onClick 事件處理函示 focusTextInput() { // this.textInput 指向輸入框 DOM 元素 // 執行 focus() DOM API 來聚焦游標 this.textInput.focus(); } render() { // 用 ref callback function 將輸入框的參照 (reference) 存到元件實例的 this.textInput 屬性上 return ( <div> <input type="text" ref={(input) => { this.textInput = input; }} /> <br /><br /> <input type="button" value="點我 focus 輸入欄位" onClick={this.focusTextInput} /> </div> ); } } ReactDOM.render( <CustomTextInput />, document.getElementById('root') );
React 會在元件 mount 時,執行
ref
callback 並傳進去 DOM element;而在 unmount 時,會再執行ref
callback 並傳進去null
。而ref
的執行時間點,是在 componentDidMount 或 componentDidUpdate lifecycle hooks 被觸發之前。Class Component
當
ref
是用在 component 上,callback function 被執行時,傳進去的參數是該元件的實例 (instance),通常是用來讓父元件可以直接存取子元件的屬性 (property) 或函式 (method)。例如延續上面的例子,我們想在元件被渲染到畫面時,自動 focus 在輸入欄位上:
class AutoFocusTextInput extends React.Component { // 當元件掛載到 DOM 時 // 執行 CustomTextInput 元件的 focusTextInput() 方法 focus 欄位 componentDidMount() { this.textInput.focusTextInput(); } render() { // 將 CustomTextInput 元件的 reference 存到 this.textInput return ( <CustomTextInput ref={(input) => { this.textInput = input; }} /> ); } } ReactDOM.render( <AutoFocusTextInput />, document.getElementById('root') );
如果你想將子元件中的某個 DOM reference 讓父元件能直接存取,你也可以用這種常用的寫法:
// 這種用法就可以用在 Functional Component
function CustomTextInput(props) {
return (
<div>
// ref 設定父元件傳進來的 callback props
// ref 執行時會自動將 DOM element reference 傳進去
<input ref={props.inputRef} />
</div>
);
}
class Parent extends React.Component {
render() {
return (
// 從父元件傳進一個 callback props
// 用來讓子元件的 ref 執行,並傳進 reference
// 父元件在 callback 中,將 reference 存到自己的 this.inputElement 屬性上
// P.S. inputRef 這名稱是自己可以隨便取的
<CustomTextInput
inputRef={el => this.inputElement = el}
/>
);
}
}
最後提醒大原則上,能不用 ref 就儘量不用,因為容易破壞元件的獨立性和封裝性,在大多數的情況下,你可以透過傳遞 props 或提升共用狀態來解決你的問題。