我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

本篇文章的使用场景对于一般人来说,并不常见。因为是用于自定义组件使用的,意思是提供react的各自控件给外部调用的时候,才会用的到forwardRef【而且还可以被普通的ref替代】。而对于控件使用者来说,forwardRef是完全无感的,用到的只有常规的ref

苏南大叔:react教程,forwardRef()如何使用?ref如何跨组件传递? - forward-hero
react教程,forwardRef()如何使用?ref如何跨组件传递?(图5-1)

苏南大叔的程序如此灵动博客,这里记录苏南大叔和计算机代码的故事。本文描述react项目的forwardRef的使用方法。测试环境:create-react-app@5.0.1react@18.2.0react-dom@18.2.0node@16.14.2

本文主要介绍做组件库时会用到的forwardRef函数,普通情况下是用不到的。平时使用的是ref,请参考:
https://newsn.net/say/react-ref.html

ref不能通过props传递,但是【非ref】可以

由于ref在使用方式上,和普通的props是一样的。

  • 对于子组件来说,是否可以通过props.ref获得ref引用呢?答案是不能。
  • 那么,如果把ref改名成ref2,是否可以获得props.ref2呢?答案是可以。

测试用例为Sunan1

export class Sunan1 extends React.Component {
    state = {who: "苏南大叔"};
    componentDidMount() {
        console.log(this.props)
    }
    render() {
        return (<div ref={this.props.ref2}>一号子组件(类组件)</div>);
    }
}

调用方式:

<Sunan1 ref={ref1} ref2={ref2} sn="SN" />

测试props输出了ref2sn这两个props,并且ref2成功作用于div上,并没有输出预期的ref属性。

苏南大叔:react教程,forwardRef()如何使用?ref如何跨组件传递? - app-ref-useref
react教程,forwardRef()如何使用?ref如何跨组件传递?(图5-2)

ref是个被特殊处理的关键字,ref引用指代了子组件本身,在父组件中可以使用ref获得子组件内部数据。比如:

<button onClick={() => { console.log(ref1.current.state.who) }}>1号组件</button>

函数组件不能直接使用ref

如果在函数式组件实例上,使用ref,则会有报错信息。
组件定义:

const Sunan2 = (props) => {
  console.log(props);
  return (<div ref={props.ref}>函数组件与ref</div>);
}

组件调用:

function App() {
  const ref1 = useRef(null);
  return (
    <div className="App">
      <Sunan2 ref={ref1}>
    </div>
  );
}
export default App;

这个时候会有警告信息提示:

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

大意是:ref属性是无法叠加于函数式组件上的,【注意:ref可以直接作用于类组件】。写了ref也没用,但是可以使用React.forwardRefref属性给再加工一下。

苏南大叔:react教程,forwardRef()如何使用?ref如何跨组件传递? - 报错信息函数组件
react教程,forwardRef()如何使用?ref如何跨组件传递?(图5-3)

`ref` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop.

大意是:在组件里面不能使用props.ref

函数组件可以使用非ref字样

那么,把例子中的ref字样换成ref2字样呢?
组件定义:

const Sunan2 = (props) => {
  console.log(props);
  return (<div ref={props.ref2}>函数组件与ref</div>);
}

组件调用:

function App() {
  const ref2 = useRef(null);
  return (
    <div className="App">
      <Sunan2 ref2={ref2}>
    </div>
  );
}
export default App;

结果一切正常,可以正常使用。这是不是比较神奇了。当然,这里在子组件里面,是使用props.ref2获得的ref定义。

forwardRef()

目前的基本结论是:使用ref字样,就会被系统自动过滤掉。如果换成非ref字样,比如ref2字样,就可以通过props.来获取并正常使用ref,但是显然这并不是react希望大家这么做的,官方推荐使用的是:forwardRef()

子组件定义:

export const Sunan3 = forwardRef(function (props, ref) {
    console.log("forwardRef(函数组件)", props, ref);
    return (
        <>
            <input type="text" ref={ref} defaultValue="forwardRef(函数组件)" />
        </>
    )
});

class Sunan4_ extends React.Component {
    componentDidMount() {
        console.log("forwardRef(类式组件)", this.props)
    }
    render() {
        return (
            <div>
                <span ref={this.props.ref2}>forwardRef(类式组件)</span>
            </div>
        )
    }
};
export const Sunan4 = forwardRef((props, ref) => <Sunan4_ ref2={ref} {...props} />);

苏南大叔:react教程,forwardRef()如何使用?ref如何跨组件传递? - forwardref使用方式
react教程,forwardRef()如何使用?ref如何跨组件传递?(图5-4)

组件调用:

<Sunan3 ref={ref3} />
<Sunan4 ref={ref4} />
<button onClick={() => { console.log(ref4.current.innerHTML) }}>4号组件</button>

forwardRef相关总结

组件forward中间件ref子组件的父组件传递ref子组件的父组件传递ref2
函数组件警告,系统强制过滤子组件props.ref2
类式组件指代子组件,可访问子组件内部子组件this.props.ref2
forwardRef(函数组件)直接传入子组件使用子组件接收两个参数props,ref子组件props.ref2
forwardRef(类式组件)必须改名非ref只在forwardRef()函数内有效子组件this.props.ref2

完整代码

苏南大叔:react教程,forwardRef()如何使用?ref如何跨组件传递? - forwardref截图
react教程,forwardRef()如何使用?ref如何跨组件传递?(图5-5)

App.js

import React, { createRef, useRef } from "react";
import { Sunan1, Sunan2, Sunan3, Sunan4 } from "./SunanLib";

function App() {
  const ref1 = useRef(null);
  const ref2 = useRef(null);
  const ref3 = useRef(null);
  const ref4 = useRef(null);
  return (
    <div className="App">
      <div>
        <Sunan1 ref={ref1} ref2={ref2} sn="SN" />
        <button onClick={() => { console.log(ref1.current.state.who) }}>1号组件</button>
      </div>
      <div>
        <Sunan2 ref={ref1} ref2={ref2} sn="SN" />
      </div>
      <div>
        <Sunan3 ref={ref3} /></div>
      <div>
        <Sunan4 ref={ref4} />
        <button onClick={() => { console.log(ref4.current.innerHTML) }}>4号组件</button>
      </div>
    </div>
  );
}
export default App;

SunanLib.js

import React, { forwardRef } from "react";

export class Sunan1 extends React.Component {
    state = {who: "苏南大叔"};
    componentDidMount() {
        console.log(this.props)
    }
    render() {
        return (<div ref={this.props.ref2}>一号子组件(类组件)</div>);
    }
}

export const Sunan2 = (props) => {
    console.log(props);
    return (<div ref={props.ref2}>二号子组件(函数组件)</div>);
}

export const Sunan3 = forwardRef(function (props, ref) {
    console.log("forwardRef(函数组件)", props, ref);
    return (
        <>
            <input type="text" ref={ref} defaultValue="forwardRef(函数组件)" />
        </>
    )
});

class Sunan4_ extends React.Component {
    componentDidMount() {
        console.log("forwardRef(类式组件)", this.props)
    }
    render() {
        return (
            <div>
                <span ref={this.props.ref2}>forwardRef(类式组件)</span>
            </div>
        )
    }
};
export const Sunan4 = forwardRef((props, ref) => <Sunan4_ ref2={ref} {...props} />);

结束语

更多react经验文章,请点击苏南大叔的博客文章:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   react