[1]使用場景 [2]ref [3]非受控組件 [4]ReactDOM ...
前面的話
某些情況下需要在典型數據流外強制修改子代。要修改的子代可以是 React 組件實例,也可以是 DOM 元素。這時就要用到refs來操作DOM
使用場景
下麵是幾個適合使用 refs 的情況
1、處理焦點、文本選擇或媒體控制
2、觸發強制動畫
3、集成第三方 DOM 庫
如果可以通過聲明式實現,則儘量避免使用 refs
[註意]不要在 Dialog
組件上直接暴露 open()
和 close()
方法,最好傳遞 isOpen
屬性
ref
React 支持給任意組件添加特殊屬性。ref
屬性接受一個回調函數,它在組件被載入或卸載時會立即執行
[註意]在組件mount之後再去獲取ref。componentWillMount和第一次render時都獲取不到,在componentDidMount才能獲取到
【HTML元素】
當給 HTML 元素添加 ref
屬性時,ref
回調接收了底層的 DOM 元素作為參數
React 組件在載入時將 DOM 元素傳入 ref
的回調函數,在卸載時則會傳入 null
。ref
回調會在componentDidMount
或 componentDidUpdate
這些生命周期回調之前執行。
class CustomTextInput extends React.Component { constructor(props) { super(props); this.focus = this.focus.bind(this); } focus() { this.textInput.focus(); } render() { return ( <div> <input type="text" ref={(input) => { this.textInput = input; }} /> <input type="button" value="Focus the text input" onClick={this.focus} /> </div> ); } }
更簡短的寫法如下
ref={input => this.textInput = input}
【類組件】
當 ref
屬性用於使用 class 聲明的自定義組件時,ref
的回調接收的是已經載入的 React 實例
class AutoFocusTextInput extends React.Component { componentDidMount() { this.textInput.focusTextInput(); } render() { return ( <CustomTextInput ref={(input) => { this.textInput = input; }} /> ); } }
[註意]這種方法僅對 class
聲明的 CustomTextInput
有效
【函數式組件】
不能在函數式組件上使用 ref 屬性,因為它們沒有實例
【對父組件暴露DOM節點】
在子節點上暴露一個特殊的屬性。子節點將會獲得一個函數屬性,並將其作為 ref
屬性附加到 DOM 節點。這允許父代通過中間件將 ref
回調給子代的 DOM 節點
該方法適用於類組件和函數式組件
function CustomTextInput(props) { return ( <div> <input ref={props.inputRef} /> </div> ); } class Parent extends React.Component { render() { return ( <CustomTextInput inputRef={el => this.inputElement = el} /> ); } }
在上面的例子中,Parent
將它的 ref 回調作為一個特殊的 inputRef
傳遞給 CustomTextInput
,然後 CustomTextInput
通過 ref
屬性將其傳遞給 <input>
。最終,Parent
中的 this.inputElement
將被設置為與 CustomTextInput
中的 <input>
元素相對應的 DOM 節點
非受控組件
要編寫一個非受控組件,而非為每個狀態更新編寫事件處理程式,可以使用 ref 從 DOM 獲取表單值
[註意]可能通過e.target.value取得DOM值,而不用綁定react
class NameForm extends React.Component { constructor(props) { super(props); this.handleSubmit = this.handleSubmit.bind(this); } handleSubmit(event) { alert('A name was submitted: ' + this.input.value); event.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit}> <label> Name: <input type="text" ref={(input) => this.input = input} /> </label> <input type="submit" value="Submit" /> </form> ); } }
由於非受控組件將真實數據保存在 DOM 中,因此在使用非受控組件時,更容易同時集成React和非React代碼
【預設值】
在 React 的生命周期中,表單元素上的 value
屬性將會覆蓋 DOM 中的值。使用非受控組件時,通常希望 React 可以為其指定初始值,但不再控制後續更新。要解決這個問題,可以指定一個 defaultValue
屬性而不是 value
render() { return ( <form onSubmit={this.handleSubmit}> <label> Name: <input defaultValue="Bob" type="text" ref={(input) => this.input = input} /> </label> <input type="submit" value="Submit" /> </form> ); }
同樣,<input type="checkbox">
和 <input type="radio">
支持 defaultChecked
,<select>
和 <textarea>
支持 defaultValue
ReactDOM
react-dom這個軟體包提供了針對DOM的方法,可以在應用的頂級域中調用,也可以在有需要的情況下用作跳出React模型的出口。但大部分組件都不應該需要使用這個包
render()
unmountComponentAtNode()
findDOMNode()
【render()】
ReactDOM.render(
element,
container,
[callback]
)
渲染一個React元素,添加到位於提供的container里的DOM元素中,並返回這個組件的一個 引用 (或者對於無狀態組件返回null)
如果這個React元素之前已經被渲染到container里去了,這段代碼就會進行一次更新,並且只會改變那些反映元素最新狀態所必須的DOM元素
【unmountComponentAtNode()】
ReactDOM.unmountComponentAtNode(container)
從DOM元素中移除已掛載的React組件,清除它的事件處理器和state。如果容器內沒有掛載任何組件,這個函數什麼都不會幹。 有組件被卸載的時候返回true,沒有組件可供卸載時返回 false
【findDOMNode()】
ReactDOM.findDOMNode(component)
如果這個組件已經被掛載到DOM中,函數會返回對應的瀏覽器中生成的DOM元素 。需要從DOM中讀取值時,比如表單的值,或者計算DOM元素的尺寸,這個函數會非常有用。 大多數情況下,可以添加一個指向DOM節點的引用,從而完全避免使用findDOMNode 這個函數。當 render 返回 null 或者 false 時, findDOMNode 也返回 null