React forwardRef的使用方法及注意点


Posted in Javascript onJune 13, 2021

之前使用react.forwardRef始终无法应用于react高阶组件中,最近终于捣鼓出来了,于是记录下来。关键点就是React.forwardRef的API中ref必须指向dom元素而不是React组件。

React.forwardRef使用示例

下面就是应用到React组件的错误示例:

const A=React.forwardRef((props,ref)=><B {...props} ref={ref}/>)

这就是我之前经常犯的错误, 这里的ref是无法生效的。

前面提到ref必须指向dom元素,那么正确方法就应用而生:

const  A=React.forwardRef((props,ref)=>(
<div ref={ref}>
<B {...props} />
</div>
))

作用与注意点

  1. 父组件创建一个ref对象,绑定给子组件中的Dom元素或class组件
  2. 函数组件是没有实例的
  3. 高阶组件需做特殊处理

父组件获取子组件中Dom元素实例

React forwardRef的使用方法及注意点

import React, { useRef } from 'react';
import Content from './content';

const Home = () => {
  // 创建一个Ref对象
  const connectRef = useRef(null);

  const handleFoucus = () => {
    const _ref = connectRef.current;
    _ref.focus();
  };

  return (
    <div>
        <button onClick={() => handleFoucus()}>
          使用子组件中DOM元素的方法
        </button>

        <Content ref={connectRef} />
    </div>
  );
};

export default Home;
import React, { forwardRef } from 'react';

/**
 * forwardRef包裹后,ref会作为第二个参数,接收传进来的ref属性
 * e.g.
 * <Content count={count} user={user} ref={connectRef}>
 *
 * @param props - {count, user}
 * @param ref   - connectRef
 * */
const Content = (props, ref) => {
  return (
    <div>
   	  {/* 把ref绑定给传进来的ref ≈ ref={connectRef} */}
      <input type="password" ref={ref} />
    </div>
  )
};

export default forwardRef(Content);

父组件获取子组件中class组件实例

React forwardRef的使用方法及注意点

import React, { useRef } from 'react';
import Content from './content';

const Home = () => {
  // 创建一个Ref对象
  const connectRef = useRef(null);

  const handleAdd = () => {
    const _ref = connectRef.current;

    const { count } = _ref.state;
    _ref.setState({
      count: count + 1
    })
  };

  return (
    <div>
        <button onClick={() => handleAdd()}>
          使用子组件中class组件的属性和方法
        </button>

        <Content ref={connectRef} />
    </div>
  );
};

export default Home;
import React, { forwardRef } from 'react';
import Header from './header';
import Footer from './footer';

/**
 * forwardRef包裹后,ref会作为第二个参数,接收传进来的ref属性
 * e.g.
 * <Content count={count} user={user} ref={connectRef}>
 *
 * @param props - {count, user}
 * @param ref   - connectRef
 * */
const Content = (props, ref) => {
  return (
    <div>
      {/* 把ref绑定给传进来的ref ≈ ref={connectRef} */}
      <Header ref={ref} />  {/* class组件 */}
		
      {/* <Footer ref={ref} /> 函数组件是没有实例的,所以connectRef.current: null */}
    </div>
  )
};

export default forwardRef(Content)
import React from 'react';

export default class Header extends React.Component {
  state = {
    count: 0
  };

  render() {
    return (
      <div>
        {this.state.count}
      </div>
    )
  }
};

高阶组件中的特殊情况

高阶组件会把所有接收到的props,传递给被包装的组件(透传)
ref 和 key 类似,不是一个prop,所以不会透传,ref会绑定到外层的包装容器上

/*
  处理ref
  e.g. Hoc1(Hoc2(Content))

  <Content ref={myRef} /> 给Content绑定的ref会绑定到Hoc1上,且不会继续向下传递

  第一种方法 React.forwardRef ===============

      在 Hoc1外面 用React.forwardRef()对ref做处理,用props来传递ref
      0. 在高阶组件外面包裹forwardRef,拦截获取ref,增加一个props(xxx={ref}),真实组件通过props.xxx获取
      1. 使用时传 ref={XXXX}  // 和第二种方法不同的地方
      2. 用forwardRef的第二个参数获取 ref
      3. 增加一个新的props,用来向下转发ref  e.g. forwardedRef={ref}
      4. 真实组件中绑定 ref={props.forwardedRef}

      const Home = (props) => {
        const connectRef = useRef(null);

        return (
          <div>
            <Content ref={connectRef} />
          </div>
        );
      };

      // 被包装组件
      const Content = (props) => {
        return (
          <div>
            <input type="password" ref={props.forwardedRef} />
          </div>
        );
      };


      // forwardRef的第二个入参可以接收ref,在Hoc外层对ref做处理
      export default React.forwardRef((props, ref) => {
        const Wrapper = React.memo(Content);  // Hoc

        // forwardRef包裹的是Wrapper
        // 需要在Wrapper中把ref向下传递给真实组件
        // Wrapper中增加一个props属性,把ref对象作为props传给子组件
        return <Wrapper {...props} forwardedRef={ref} />;
      });

  第二种方法 ==========

  0. 使用时就用一个props来保存ref
  1. 使用时传 xxx={ref}  // 和第一种方法的不同点
  2. 真实组件中绑定 ref={props.xxx}

  const Home = (props) => {
    const connectRef = useRef(null);

    return (
      <div>
        <Content forwardedRef={connectRef} />
      </div>
    );
  };

  // 定义高阶组件
  export const Hoc = (WrappedComponent) => {
    class Wrapper extends React.Component {
      render() {
        return <WrappedComponent {...props} />
      }
    }
  }

  // 被包装的组件
  const Content = (props) => {
    return (
      <div>
        <input type="password" ref={props.forwardedRef} />
      </div>
    );
  };

  // 包装过程
  export default Hoc(Content);

* */

以上就是React forwardRef的使用方法及注意点的详细内容,更多关于React forwardRef使用的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
跟着JQuery API学Jquery 之三 筛选
Apr 09 Javascript
如何使用Jquery获取Form表单中被选中的radio值
Aug 09 Javascript
jQuery把表单元素变为json对象
Nov 06 Javascript
windows8.1+iis8.5下安装node.js开发环境
Dec 12 Javascript
jquery禁止回车触发表单提交
Dec 12 Javascript
JS动态加载当前时间的方法
Feb 09 Javascript
js获取元素的外链样式的简单实现方法
Jun 06 Javascript
js仿腾讯QQ的web登陆界面
Aug 19 Javascript
vue.js 初体验之Chrome 插件开发实录
May 13 Javascript
jquery与js实现全选功能的区别
Jun 11 jQuery
cordova+vue+webapp使用html5获取地理位置的方法
Jul 06 Javascript
利用js canvas实现五子棋游戏
Oct 11 Javascript
原生Javascript+HTML5一步步实现拖拽排序
JS代码编译器Monaco使用方法
React中的Context应用场景分析
Jun 11 #Javascript
详解JVM系列之内存模型
使用Vue3+Vant组件实现App搜索历史记录功能(示例代码)
一文搞懂redux在react中的初步用法
Jun 09 #Javascript
深入详解JS函数的柯里化
Jun 09 #Javascript
You might like
浅谈PHP中JSON数据操作
2015/07/01 PHP
PHP利用APC模块实现大文件上传进度条的方法
2015/10/29 PHP
php下载文件,添加响应头的简单实例
2016/09/22 PHP
PHP批量修改文件名称的方法分析
2017/02/27 PHP
php提交表单时保留多个空格及换行的文本样式的方法
2017/06/20 PHP
PHP安装memcache扩展的步骤讲解
2019/02/14 PHP
JavaScript 页面编码与浏览器类型判断代码
2010/06/03 Javascript
onclick与listeners的执行先后问题详细解剖
2013/01/07 Javascript
JavaScript异步编程Promise模式的6个特性
2014/04/03 Javascript
深入理解JavaScript系列(28):设计模式之工厂模式详解
2015/03/03 Javascript
jQuery在ul中显示某个li索引号的方法
2015/03/17 Javascript
高效Web开发的10个jQuery代码片段
2016/07/22 Javascript
js添加千分位的实现代码(超简单)
2016/08/01 Javascript
AngulaJS路由 ui-router 传参实例
2017/04/28 Javascript
微信JS SDK接入的几点注意事项(必看篇)
2017/06/23 Javascript
Swiper实现轮播图效果
2017/07/03 Javascript
iconfont的三种使用方式详解
2018/08/05 Javascript
AngularJS 事件发布机制
2018/08/28 Javascript
Vue+Element实现表格编辑、删除、以及新增行的最优方法
2019/05/28 Javascript
ElementUI Tree 树形控件的使用并给节点添加图标
2020/02/27 Javascript
vue将文件/图片批量打包下载zip的教程
2020/10/21 Javascript
[01:00:25]NB vs Secret 2018国际邀请赛小组赛BO1 B组加赛 8.19
2018/08/21 DOTA
Python网络爬虫出现乱码问题的解决方法
2017/01/05 Python
Python中scatter函数参数及用法详解
2017/11/08 Python
python实现用类读取文件数据并计算矩形面积
2020/01/18 Python
Python类的继承super相关原理解析
2020/10/22 Python
Canvas制作旋转的太极的示例
2018/03/09 HTML / CSS
松本清官方海外旗舰店:日本最大的药妆连锁店
2017/11/21 全球购物
意大利比基尼品牌:MISS BIKINI
2019/11/02 全球购物
如何将字串String转换成整数int
2015/02/21 面试题
中学生自我鉴定
2014/02/04 职场文书
三年级学生评语大全
2014/12/26 职场文书
2015年党支部公开承诺书
2015/01/22 职场文书
工作失职自我检讨书
2015/05/05 职场文书
学习型家庭事迹材料(2016精选版)
2016/02/29 职场文书
如何写好闭幕词
2019/04/02 职场文书