React中Ref使用详解(createRef、回调ref、useRef、forwardRef、useImperativeHandle)
目录类组件中使用ref='myRef'(React中不推荐)createRef(推荐)回调Ref (推荐)函数组件中使用React.useRefReact.forwardRefReact.useImperativeHandle由于函数组件中没有实例,所以区分2种用法。类组件中使用注意你不能在函数组件上使用下面的ref属性,因为他们没有实例。ref='myRef'(React中不推荐)和vue中的r
目录
由于函数组件中没有实例,所以区分2种用法。
类组件中使用
注意你不能在函数组件上使用下面的ref
属性,因为他们没有实例。
ref='myRef'(React中不推荐)
和vue中的ref方法使用相同,在元素上添加ref=‘myRef’,然后通过this.refs.myRef访问绑定的dom节点。不推荐使用,且会有一大堆飘红,但还是可以用。
createRef(推荐)
创建一个能够通过 ref 属性附加到 React 元素的 ref,需要通过点current访问dom节点或组件实例。
ref 的值根据节点的类型而有所不同:
- 当
ref
属性用于 HTML 元素时,构造函数中使用React.createRef()
创建的ref
接收底层 DOM 元素作为其current
属性(例如下面this.inputRef.current为input的dom节点)。 - 当
ref
属性用于自定义 class 组件时,ref
对象接收组件的挂载实例作为其current
属性。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
render() {
return <input type="text" ref={this.inputRef} />;
}
componentDidMount() {
this.inputRef.current.focus();
}
}
回调Ref (推荐)
它能助你更精细地控制何时 refs 被设置和解除。React 将在组件挂载时,会调用 ref
回调函数并传入 DOM 元素,当卸载时调用它并传入 null
。
在更新过程中它会被执行两次,第一次传入参数 null
,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。所以在 componentDidMount
或 componentDidUpdate
触发前,React 会保证 refs 一定是最新的。
注意这里的ref不用通过点current,直接是doom节点或组件的实例。
import React, { PureComponent } from 'react';
class App extends PureComponent {
constructor(props) {
super(props)
this.titleRef = null // 定义一个初始值
}
render() {
return (
<div>
<h1 ref={arg => this.titleRef = arg}>Hello,React</h1>
<button onClick={e => this.changeText()}>函数改变文本</button>
</div>
);
}
changeText() {
console.log(this.titleRef)
this.titleRef.innerHTML = "Hello,yf"
}
}
export default App;
函数组件中使用
refs组件中的使用class组件的使用,函数组件是不支持的,因为函数组件是没有实例的。
React.useRef
useRef
返回一个可变的 ref 对象,其 .current
属性被初始化为传入的参数(initialValue通常为null
)。返回的 ref 对象在组件的整个生命周期内持续存在。
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
当 ref 对象内容发生变化时,useRef
并不会通知你。变更 .current
属性不会引发组件重新渲染。如果想要在 React 绑定或解绑 DOM 节点的 ref 时运行某些代码,则需要使用回调ref来实现
React.forwardRef
将子组件ref暴露出去,在父组件上绑定ref获取子组件内绑定的ref。
函数组件通过React.forwardRef创建后,才可以在该组件上绑定ref属性,并且ref属性绑定的值是该组件内的ref绑定的dom节点而非该组件的实例。在该组件内ref只是回调ref函数,并不能在组件内访问。
注意下面的Test才是组件名。
import React, { PureComponent, forwardRef } from "react";
const Test = forwardRef(function Profile(props, ref) {
React.useEffect(() => {
console.log(ref); //arg => this.titleRef = arg
}, []);
return (
<>
<h1>h1</h1>
<h2 ref={ref}>h2</h2>
</>
);
});
class App extends PureComponent {
constructor(props) {
super(props);
this.titleRef = null; // 定义一个初始值
}
render() {
return (
<div>
<Test ref={(arg) => (this.titleRef = arg)}></Test>
<button onClick={(e) => this.changeText()}>函数改变文本</button>
</div>
);
}
changeText() {
console.log(this.titleRef);
this.titleRef.innerHTML = "Hello wrld";
}
}
export default App;
React.useImperativeHandle
搭配forwardRef一起使用,这是暴露在父组件的ref是useImperativeHandle函数第二个参数返回的对象。
import React, { PureComponent, forwardRef } from "react";
function FancyInput(props, ref) {
const inputRef = React.useRef();
React.useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
a(){
console.log(inputRef)
}
}));
return <input ref={inputRef }/>;
}
FancyInput = forwardRef(FancyInput);
class App extends PureComponent {
constructor(props) {
super(props);
this.fancyRef = null; // 定义一个初始值
}
render() {
return (
<>
<FancyInput ref={(arg) => (this.fancyRef = arg)} />
<div onClick={()=>{this.changeText()}}>click</div>
</>
);
}
changeText() {
console.log(this.fancyRef); //{focus: ƒ, a: ƒ}
this.fancyRef.focus() //input聚焦
}
}
export default App;
更多推荐
所有评论(0)