某些情況下需要在典型數(shù)據(jù)流外強制修改子代旦棉。要修改的子代可以是React組件實例锅论,也可以是DOM元素。這時就要用到 refs 來操作DOM
使用場景
下面是幾個適合使用 refs 的情況:
- 處理焦點铸史,文本選擇或媒體控制
- 觸發(fā)強制動畫
- 集成第三方DOM庫
如果可以通過聲明式實現(xiàn)庐橙,則盡量避免使用 refs惯吕。注意,不要在 Dialog 組件上直接暴露 open() 和 close() 方法怕午,最好傳遞 isOpen 屬性
ref
react 支持給任意組件添加特殊屬性。ref 屬性接受一個回調(diào)函數(shù)淹魄,它在組件被加載或卸載時會立即執(zhí)行郁惜。注意,在組件 mount 之后再去獲取ref甲锡。componentWillMount 和第一次 render 時都獲取不到兆蕉,在 componentDidMount 才能獲取到。
html元素
當(dāng)給HTML元素添加 ref 屬性時缤沦,ref 回調(diào)接收了底層的DOM元素作為參數(shù)虎韵。react 組件在加載時將DOM元素傳入ref的回調(diào)函數(shù),在卸載時則會傳入 null缸废。ref 回調(diào)會在 componentDidMount 或 componentDidUpdate 這些生命周期回調(diào)之前執(zhí)行包蓝。
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 }
類組件
當(dāng) ref 屬性用于使用class聲明的自定義組件時,ref的回調(diào)接收的是已經(jīng)加載的React實例企量。
class AutoFocusTextInput extends React.Component {
componentDidMount() {
this.textInput.focusTextInput();
}
render() {
return (
<CustomTextInput
ref={(input) => { this.textInput = input; }} />
);
}
}
注意测萎,這種方法僅對 class 聲明的 CustomTextInput 有效。
函數(shù)式組件
不能在函數(shù)式組件上使用 ref 屬性届巩,因為它們沒有實例硅瞧。
對父組件暴露DOM節(jié)點
在子節(jié)點上暴露一個特殊的屬性,子節(jié)點將會獲得一個函數(shù)屬性恕汇,并將其作為 ref 屬性附加到DOM節(jié)點腕唧。這允許父代通過中間件將 ref 回調(diào)給子代的DOM節(jié)點。
function Custom TextInput(props) {
return(
<div>
<input ref={ props.inputRef }/>
</div>
)
}
class Parent extends React.Component {
render() {
return (
<Custom TextInput
inputRef={ el => this.inputElement = el }
/>
)
}
}
在上面這個例子中瘾英,Parent 將它的 ref 回調(diào)作為一個特殊的 inputRef 傳遞給 CustomTextInput枣接,然后CustomTextInput 通過 ref 屬性將其傳遞給<input>。最終方咆,Parent 中的this.inputElement 將被設(shè)置為與CustomTextInput 中的<input>元素相對應(yīng)的DOM節(jié)點月腋。
非受控組件
要編寫一個受控組件,而非為每個狀態(tài)更新編寫事件處理程序,可以使用 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 }>
<lable>
Name: <input type="text"ref={ (input) => this.input = input } />
</lable>
<input type="submit" value="Submit" />
</form>
);
}
}
由于非受控組件將真實數(shù)據(jù)保存在DOM中捌省,因此在使用非受控組件時,更容易同時集成React和非React代碼碉钠。
默認(rèn)值
在React的生命周期中纲缓,表單元素上的value屬性將會覆蓋DOM中的值。使用非受控組件時喊废,通常希望React可以為其指定初始值祝高,但不再控制后續(xù)更新。更解決這個問題污筷,可以指定一個 defaultValue屬性而不是value
render() {
return(
<form onSubmit={this.handleSubmit}>
<lable>
Name: <input dafaultValue="Bob" type="text" ref={ (input) => this.input = input } />
</lable>
<input type="submit" value="Submit" />
</form>
);
}
同樣工闺,<input type="checkbox">和<input type="radio">支持defaultChecked,<select>和<textarea>支持defaultValue瓣蛀。
ReactDOM
react-dom 這個軟件包提供了針對DOM的方法陆蟆,可以在應(yīng)用的頂級域中調(diào)用,也可以在有需要的情況下用作跳出React模型的出口惋增。但大部分組件都不應(yīng)該需要使用這個包叠殷。
render()
unmountComponentAtNode()
findDOMNode()
render()
ReactDOM.render(
element,
container,
[callback]
)
渲染一個react元素,添加到位于提供的container里的DOM元素中诈皿,并返回這個組件的一個引用(或者對于無狀態(tài)組件返回null)林束。如果這個react元素之前已經(jīng)被渲染到container里去了,這段代碼就會進行一次更新纫塌,并且只會改變那些反映元素最新狀態(tài)所必須的DOM元素诊县。
unmountComponentAtNode()
ReactDOM.unmountComponentAtNode(container)
從DOM元素中移除已掛載的React組件,清除它的事件處理器和state措左。如果容器內(nèi)沒有掛載任何組件依痊,這個函數(shù)什么都不會干。有組件被卸載的時候返回true怎披,沒有組件可供卸載時返回false
findDOMNode()
ReactDOM.findDOMNode(component)
如果這個組件已經(jīng)被掛載到DOM中胸嘁,函數(shù)會返回對應(yīng)的瀏覽器中生成的DOM元素。需要從DOM中讀取值時凉逛,比如表單的值性宏,或者計算DOM元素的尺寸,這個函數(shù)會非常有用状飞。大多數(shù)情況下毫胜,可以添加一個指向DOM節(jié)點的引用书斜,從而完全避免使用findDOMNode這個函數(shù)。當(dāng)render返回null或者false時酵使,findDOMNode也返回null
新 ref
版本16.3之前荐吉,React 有兩種提供ref的方式:字符串和回調(diào),因為字符串的方式有些問題口渔,所以官方建議使用回調(diào)來使用ref样屠。而現(xiàn)在引入的createRef API,據(jù)官方說是一種零缺點的使用ref的方式缺脉,回調(diào)方式也可以讓讓路了痪欲。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
然后使用current屬性,即可獲得當(dāng)前元素 this.myRef.current攻礼,典型應(yīng)用如下所示:
constructor(props) {
super(props);
this.Mask = React.createRef()
this.MenuList = React.createRef()
}
handleClick = () => {
ReactDOM.findDOMNode(this.MenuList.current).classList.toggle("transform-zero")
ReactDOM.findDOMNode(this.Mask.current).classList.toggle("mask-show")
}
注意业踢,使用 styleComponents 樣式化的元素暴露的接口是 innerRef,而不是ref
<Wrap innerRef={ this.itemRef }>