通常狀況在一個正常的dataflow中,父組件老是將data經過props傳給子組件,而後子組件re-render。可是有的時候咱們想當即修改一個react子組件或一個DOM,那該怎麼辦呢?node
對的,就是用refsreact
refs提供了一個DOM節點或react組件實例的引用,這樣咱們在組件中就能訪問到那個DOM節點或react組件實例dom
不要使用ref去作能夠聲名的事情。比方說不要讓Dialog組件暴露open()和close()方法,而是使用props.isOpen去管理Dialog函數
用React.createRef()建立refs的,而後經過ref屬附加給一個React元素或者DOM。在構造組件時,一般將Refs分配給實例屬性,以即可以在整個組件中引用它們。動畫
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />; } } 複製代碼
在生命週期方法或者事件處理器中可使用 current 屬性訪問refthis
const node = this.myRef.current
複製代碼
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// 1. 首先建立ref
this.textInput = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// 3. 最後,咱們在事件處理器中就能使用原生的DOM方法了
this.textInput.current.focus();
}
render() {
// 2. 而後,告訴react咱們但願把構造器裏定義的textInput 指向 <input>
return (
<div>
<input
type="text"
ref={this.textInput} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
複製代碼
componentDidMount
or componentDidUpdate
以前更新。比方說咱們想要一個AutoFocusTextInput組件,這個組件就是在mount以後調一下CustomTextInput的focusTextInput()方法:spa
class AutoFocusTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
componentDidMount() {
this.textInput.current.focusTextInput();
}
render() {
return (
<CustomTextInput ref={this.textInput} /> ); } } 複製代碼
注意CustomTextInput
必須是class型組件code
render的時候不能給一個function組件加refcomponent
function MyFunctionComponent() {
return <input />;
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
render() {
// This will *not* work!
return (
<MyFunctionComponent ref={this.textInput} />
);
}
}
複製代碼
可是一個function組件內部仍是能用ref的,只要這個ref不是attach到function組件的視頻
function CustomTextInput(props) {
// textInput must be declared here so the ref can refer to it
let textInput = React.createRef();
function handleClick() {
textInput.current.focus();
}
return (
<div>
<input
type="text"
ref={textInput} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
複製代碼
想不想在父組件裏面訪問一個子DOM節點呢?
不,你不想。
由於這破壞了組件的封裝。
但有些時候仍是能夠用的,好比說觸發一個focus或者測量DOM節點的大小和屬性。
上面給class組件加ref的例子,有的時候也不是最理想的,由於這個時候返給咱們的是一個react組件,而不是一個DOM。
forward refs能讓組件選擇暴露哪些子組件的refs供本身用。
建議不要暴露DOM節點,但它能夠是一個escape hatch。這個方法要向子組件添加一些代碼。而後若是咱們徹底沒法控制子組件實現,最後一個選項是使用findDOMNode(),但在StrictMode中是不能用的。
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = null;
this.setTextInputRef = element => {
this.textInput = element;
};
this.focusTextInput = () => {
// Focus the text input using the raw DOM API
if (this.textInput) this.textInput.focus();
};
}
componentDidMount() {
// autofocus the input on mount
this.focusTextInput();
}
render() {
// Use the `ref` callback to store a reference to the text input DOM
// element in an instance field (for example, this.textInput).
return (
<div>
<input
type="text"
ref={this.setTextInputRef}
/>
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
複製代碼
ref不只可使用React.createRef()建立,還可使用一種叫回調ref的方法實現。咱們給render函數裏的dom加上一個回調ref,如上面的this.setTextInputRef。在掛載組件的時候react會調用回調ref,而後這個回調的參數會被react認爲是當前的dom。這上面的例子中,回調ref,也就是this.setTextInputRef將這個dom的引用賦給了this.textInput。
組件之間能夠傳回調refs。
function CustomTextInput(props) {
return (
<div>
<input ref={props.inputRef} />
</div>
);
}
class Parent extends React.Component {
render() {
return (
<CustomTextInput
inputRef={el => this.inputElement = el}
/>
);
}
}
複製代碼
Parent給 inputRef prop 賦了一個回調,而後傳給CustomTextInput,完了CustomTextInput又把這個回調給了input當作一個回調refs。這樣的結果是啥呢?是Parent裏頭的this.inputElement會賦值爲一個DOM,而這個DOM偏偏就是CustomTextInput裏頭的input
ref 回調定義爲內聯函數的話更新的時候會被調用2次,1次refs是null,1次是DOM。這是由於每次渲染都會建立一個新的函數實例,因此react須要先清除舊的ref而後再設置新的ref。
能夠把ref callback給bind到class上,不過大多數狀況下這個事影響不大