React的Refs转发用法详解!
一、什么是
ref
ref 就是DOM节点或class组件的引用,方便其他组件访问其内部方法。
1、创建
ref
创建
ref
并将其与React元素的
ref
属性进行关联。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
render() {
return <div ref={this.myRef} />;
2、访问
ref
const node = this.myRef.current;
二、用
ref
操作DOM元素
用button点击,让输入焦点回到input。复制下面代码到
index.html
,用浏览器打开即可看到效果。
<!DOCTYPE html>
<meta charset="UTF-8" />
<title>DOM元素使用ref</title>
<!-- 第一步:加载开发版本的React -->
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<!-- 部署时,请用下面链接替代上面 -->
<!-- <script src="https://unpkg.com/react@16/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js" crossorigin></script> -->
<!-- 第二步:加载开发版本的JSX -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<div id="root"></div>
<script type="text/babel">
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// 第一步:创建一个 ref 来存储 textInput 的 DOM 元素
this.textInput = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
focusTextInput() {
// 第二步:直接使用原生 API 使 text 输入框获得焦点
// 注意:我们通过 "current" 来访问 DOM 节点
this.textInput.current.focus();
render() {
// 第三步:告诉 React 我们想把 <input> ref 关联到
// 构造器里创建的 `textInput` 上
return (
<input
type="text"
ref={this.textInput} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
ReactDOM.render(<CustomTextInput />, document.getElementById('root'));
</script>
</body>
</html>
三、用
ref
操作class组件
利用ref来调用子组件的方法。复制下面代码到
index.html
,用浏览器打开即可看到效果。
<!DOCTYPE html>
<meta charset="UTF-8" />
<title>class组件使用ref</title>
<!-- 第一步:加载开发版本的React -->
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<!-- 部署时,请用下面链接替代上面 -->
<!-- <script src="https://unpkg.com/react@16/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js" crossorigin></script> -->
<!-- 第二步:加载开发版本的JSX -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<div id="root"></div>
<script type="text/babel">
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.state = { text: "" };
setTextInput(text) {
this.setState({ text: text });
render() {
return (
<input type="text" value={this.state.text} onChange={() => { }} />
class SetTextInput extends React.Component {
constructor(props) {
super(props);
// 第一步:创建Ref
this.textInput = React.createRef();
componentDidMount() {
// 第三步:渲染时,调用其他组件内的方法
this.textInput.current.setTextInput("测试");
render() {
// 第二步:创建的Ref与组件关联(赋值)
return (
<CustomTextInput ref={this.textInput} />
ReactDOM.render(<SetTextInput />, document.getElementById('root'));
</script>
</body>
</html>
四、用
ref
操作函数组件
1、错误案例
函数组件因为没有实例,默认情况下无法使用
ref
,下面代码是错误的。
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} />
2、useRef
class组件内用
React.createRef()
实现
ref
功能;而函数组件内,用Hook函数
useRef
实现
ref
功能 。复制下面代码到
index.html
,用浏览器打开即可看到效果。
<!DOCTYPE html>
<meta charset="UTF-8" />
<title>函数组件使用ref</title>
<!-- 第一步:加载开发版本的React -->
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<!-- 部署时,请用下面链接替代上面 -->
<!-- <script src="https://unpkg.com/react@16/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js" crossorigin></script> -->
<!-- 第二步:加载开发版本的JSX -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<div id="root"></div>
<script type="text/babel">
function CustomTextInput(props) {
// 这里必须声明 textInput,这样 ref 才可以引用它
const textInput = React.useRef(null);
function handleClick() {
textInput.current.focus();
return (
<input
type="text"
ref={textInput} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
ReactDOM.render(<CustomTextInput />, document.getElementById('root'));
</script>
</body>
</html>
五、什么是Refs转发
Ref 转发
是将
ref
传递给子组件的技巧,特别适用于
重用组件
。
1、ref 默认不传递给子节点
复制下面代码到
index.html
,用浏览器打开,在控制台里看输出。
<!DOCTYPE html>
<meta charset="UTF-8" />
<title>组件间传递ref</title>
<!-- 第一步:加载开发版本的React -->
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<!-- 部署时,请用下面链接替代上面 -->
<!-- <script src="https://unpkg.com/react@16/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js" crossorigin></script> -->
<!-- 第二步:加载开发版本的JSX -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<div id="root"></div>
<script type="text/babel">
class FancyButton extends React.Component {
handleClick() {
console.log('FancyButton')
render() {
return <button onClick={this.handleClick}>{this.props.label}</button>
function logProps(WrappedComponent) {
class LogProps extends React.Component {
componentDidMount(prevProps) {
console.log('old props:', prevProps)
console.log('new props:', this.props)
handleClick() {
console.log('LogProps')
render() {
return <WrappedComponent {...this.props} />;
return LogProps;
let LogProps = logProps(FancyButton);
class App extends React.Component {
constructor(props) {
super(props)
// 第一步:创建ref
this.ref = React.createRef()
componentDidMount() {
// 第三步:ref赋值给LogProps后,但LogProps并不能把ref传递给子组件FancyButton,所以只能调用LogProps的handleClick
this.ref.current.handleClick();
// 第二步:创建的Ref与组件关联(赋值)
render() {
return <LogProps label="Click Me" ref={this.ref} />
ReactDOM.render(<App />, document.getElementById('root'));
</script>
</body>
</html>
2、解决默认不转发的问题
React.forwardRef
可以利用props,把ref传递给子组件。复制下面代码到
index.html
,用浏览器打开,在控制台里看输出。
<!DOCTYPE html>
<meta charset="UTF-8" />
<title>组件间传递ref</title>
<!-- 第一步:加载开发版本的React -->
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<!-- 部署时,请用下面链接替代上面 -->
<!-- <script src="https://unpkg.com/react@16/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js" crossorigin></script> -->
<!-- 第二步:加载开发版本的JSX -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<div id="root"></div>
<script type="text/babel">
class FancyButton extends React.Component {
handleClick() {
console.log('FancyButton')
render() {
return <button onClick={this.handleClick}>{this.props.label}</button>
function logProps(WrappedComponent) {
class LogProps extends React.Component {
componentDidMount(prevProps) {
console.log('old props:', prevProps)
console.log('new props:', this.props)
handleClick() {
console.log('LogProps')
render() {
// 第五步:读取父容器的forwardedRef属性;
const { forwardedRef, ...rest } = this.props;
// 第六步:将forwardedRef与WrappedComponent的ref属性进行关联;
// 也就是跟FancyButton组件的ref进行关联。
return <WrappedComponent ref={forwardedRef} {...rest} />;
// 第四步:调用React.forwardRef,把ref赋值给LogProps的自定义属性“forwardedRef”进行传递;
// forwardedRef:此属性名可随意设置;
// {...props}:这是js的展开语法,表示把props所有值都传递给LogProps
return React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
// 第三步:调用高阶组件logProps创建新组件LogProps
let LogProps = logProps(FancyButton);
class App extends React.Component {
constructor(props) {
super(props)
// 第一步:创建ref
this.ref = React.createRef()
componentDidMount() {
// 第七步:调用ref
this.ref.current.handleClick();
// 第二步:把创建的ref传递给组件LogProps
render() {
return <LogProps label="Click Me" ref={this.ref} />
ReactDOM.render(<App />, document.getElementById('root'));
</script>
</body>
</html>
六、回调Refs
不通过
createRef()
设置ref,而是通过回调方式设置ref。复制下面代码到
index.html
,用浏览器打开即可看到效果。
<!DOCTYPE html>
<meta charset="UTF-8" />
<title>回调ref</title>
<!-- 第一步:加载开发版本的React -->
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<!-- 部署时,请用下面链接替代上面 -->
<!-- <script src="https://unpkg.com/react@16/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js" crossorigin></script> -->
<!-- 第二步:加载开发版本的JSX -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<div id="root"></div>
<script type="text/babel">
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// 第一步:申请ref,但是不赋值
this.textInput = null;
this.setTextInputRef = element => {
this.textInput = element;
this.focusTextInput = () => {
// 使用原生 DOM API 使 text 输入框获得焦点
if (this.textInput) this.textInput.focus();
componentDidMount() {
// 组件挂载后,让文本框自动获得焦点
this.focusTextInput();
render() {
// 第二步:使用回调函数setTextInputRef,将input的ref属性关联到第一步申请的ref
// 第三步:通过button的click事件,调用ref使得textinput输入聚焦
return (
<input
type="text"
ref={this.setTextInputRef}
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
ReactDOM.render(<CustomTextInput />, document.getElementById('root'));
</script>
</body>
</html>
七、组件间传递ref
通过组件的props属性,来传递设置ref,让input启动就聚焦输入光标。复制下面代码到
index.html
,用浏览器打开即可看到效果。
<!DOCTYPE html>
<meta charset="UTF-8" />
<title>组件间传递ref</title>
<!-- 第一步:加载开发版本的React -->
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<!-- 部署时,请用下面链接替代上面 -->
<!-- <script src="https://unpkg.com/react@16/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js" crossorigin></script> -->
<!-- 第二步:加载开发版本的JSX -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<div id="root"></div>
<script type="text/babel">
function CustomTextInput(props) {
// 第三步:接收到属性inputRef后,用input的ref与inputRef属性进行关联
return (
<input ref={props.inputRef} />
class Parent extends React.Component {
constructor(props) {
super(props);
// 第一步:申请ref,但不赋值;
this.inputElement = null;
this.focusTextInput = () => {
// 使用原生 DOM API 使 text 输入框获得焦点
if (this.inputElement) {
this.inputElement.focus();
componentDidMount() {
// 组件挂载后,让文本框自动获得焦点
this.focusTextInput();
// 第二步:自定义属性inputRef,把第一步申请的ref,通过props属性传递给组件CustomTextInput
render() {
return (
<CustomTextInput
inputRef={el => this.inputElement = el}