通常状况在一个正常的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上,不过大多数状况下这个事影响不大