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 相关文章推荐
用dtree实现树形菜单 dtree使用说明
Oct 17 Javascript
IFrame跨域高度自适应实现代码
Aug 16 Javascript
使用jQuery管理选择结果
Jan 20 Javascript
javascript实现tab切换的两个实例
Nov 05 Javascript
JS输出空格的简单实现方法
Sep 08 Javascript
JavaScript ES6中CLASS的使用详解
Nov 22 Javascript
vue2.0开发实践总结之疑难篇
Dec 07 Javascript
BootStrap 下拉菜单点击之后不会出现下拉菜单(下拉菜单不弹出)的解决方案
Dec 14 Javascript
如何使用Bootstrap 按钮实例详解
Mar 29 Javascript
基于 Vue.js 之 iView UI 框架非工程化实践记录(推荐)
Nov 21 Javascript
Vue.js 踩坑记之双向绑定
May 03 Javascript
Vue组件生命周期运行原理解析
Nov 25 Vue.js
原生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之第八天
2006/10/09 PHP
Thinkphp中的volist标签用法简介
2014/06/18 PHP
在Mac OS上编译安装Nginx+PHP+MariaDB开发环境的教程
2016/02/23 PHP
PHP的Yii框架中移除组件所绑定的行为的方法
2016/03/18 PHP
eclipse php wamp配置教程
2016/06/30 PHP
PHP使用xpath解析XML的方法详解
2017/05/20 PHP
jQuery图片轮播的具体实现
2013/09/11 Javascript
如何实现修改密码时密码框显示保存到cookie的密码
2013/12/10 Javascript
JS实现的一个简单的Autocomplete自动完成例子
2014/04/16 Javascript
jQuery 动态云标签插件
2014/11/11 Javascript
js实现从中间开始往上下展开网页窗口的方法
2015/03/02 Javascript
详解JavaScript语言的基本语法要求
2015/11/20 Javascript
javascript与jquery中的this关键字用法实例分析
2015/12/24 Javascript
AngularJS中如何使用$http对MongoLab数据表进行增删改查
2016/01/23 Javascript
JS 清除字符串数组中,重复元素的实现方法
2016/05/24 Javascript
js 数组当前行添加数据方法详解
2020/07/28 Javascript
使用Python的PEAK来适配协议的教程
2015/04/14 Python
pygame 精灵的行走及二段跳的实现方法(必看篇)
2017/07/10 Python
Python数据类型之Dict字典实例详解
2019/05/07 Python
python读取大文件越来越慢的原因与解决
2019/08/08 Python
django项目用higcharts统计最近七天文章点击量
2019/08/17 Python
flask框架自定义过滤器示例【markdown文件读取和展示功能】
2019/11/08 Python
keras Lambda自定义层实现数据的切片方式,Lambda传参数
2020/06/11 Python
详解CSS3新增的背景属性
2019/12/25 HTML / CSS
欧舒丹英国官网:购买欧舒丹护手霜等明星产品
2017/01/17 全球购物
精灵市场:Pixie Market
2019/06/18 全球购物
Ibatis的核心配置文件都有什么
2014/09/08 面试题
盛大笔试题
2016/11/05 面试题
Java语言程序设计测试题改错题部分
2014/07/22 面试题
农业资源与环境专业自荐信范文
2013/12/30 职场文书
党支部创先争优活动总结
2014/08/28 职场文书
2014年小学生迎国庆65周年演讲稿
2014/09/27 职场文书
2014年小学语文工作总结
2014/12/20 职场文书
大学生求职自荐信范文
2015/03/04 职场文书
浅谈JS的二进制家族
2021/05/09 Javascript
vue.js Router中嵌套路由的实用示例
2021/06/27 Vue.js