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的高性能td和input切换并可修改内容实现代码
Jan 09 Javascript
初学Jquery插件制作 在SageCRM的查询屏幕隐藏部分行的功能
Dec 26 Javascript
公共js在页面底部加载的注意事项介绍
Jul 18 Javascript
跟我学习javascript的最新标准ES6
Nov 20 Javascript
IE8 内存泄露(内存一直增长 )的原因及解决办法
Apr 06 Javascript
Javascript前端经典的面试题及答案
Mar 14 Javascript
浅谈JS 数字和字符串之间相互转化的纠纷
Oct 20 Javascript
解析Angular 2+ 样式绑定方式
Jan 15 Javascript
PostgreSQL Node.js实现函数计算方法示例
Feb 12 Javascript
Vue项目使用localStorage+Vuex保存用户登录信息
May 27 Javascript
微信公众号平台接口开发 获取access_token过程解析
Aug 14 Javascript
JS实现长图上下滚动效果
Mar 19 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获取301跳转URL简单实例
2013/12/16 PHP
跟我学Laravel之视图 &amp; Response
2014/10/15 PHP
PHP扩展程序实现守护进程
2015/04/16 PHP
PHP获取指定时间段之间的 年,月,天,时,分,秒
2016/06/05 PHP
解决form中action属性后面?传递参数 获取不到的问题
2017/07/21 PHP
php pdo连接数据库操作示例
2019/11/18 PHP
jQuery 版本的文本输入框检查器Input Check
2009/07/09 Javascript
js中将URL中的参数提取出来作为对象的实现代码
2011/08/16 Javascript
extjs tabpanel限制选项卡数量实现思路及代码
2013/04/02 Javascript
详解JavaScript中数组和字符串的lastIndexOf()方法使用
2016/03/13 Javascript
JavaScript数组合并的多种方法
2016/05/22 Javascript
几种二级联动案例(jQuery\Array\Ajax php)
2016/08/13 Javascript
Jquery遍历select option和添加移除option的实现方法
2016/08/26 Javascript
JS添加或修改控件的样式(Class)实现方法
2016/10/15 Javascript
JS实现的简易拖放效果示例
2016/12/29 Javascript
javascript阻止事件冒泡和浏览器的默认行为
2017/01/21 Javascript
vue微信分享 vue实现当前页面分享其他页面
2017/12/02 Javascript
JS中用EL表达式获取上下文参数值的方法
2018/03/28 Javascript
ECharts地图绘制和钻取简易接口详解
2019/07/12 Javascript
JavaScript 继承 封装 多态实现及原理详解
2019/07/29 Javascript
如何解决日期函数new Date()浏览器兼容性问题
2019/09/11 Javascript
vue中进行微博分享的实例讲解
2019/10/14 Javascript
Vue使用vue-draggable 插件在不同列表之间拖拽功能
2020/03/12 Javascript
解决vue项目input输入框双向绑定数据不实时生效问题
2020/08/05 Javascript
跟老齐学Python之复习if语句
2014/10/02 Python
python使用webbrowser浏览指定url的方法
2015/04/04 Python
Django继承自带user表并重写的例子
2019/11/18 Python
Django 实现 Websocket 广播、点对点发送消息的代码
2020/06/03 Python
Python return语句如何实现结果返回调用
2020/10/15 Python
python 批量将中文名转换为拼音
2021/02/07 Python
实例讲解CSS3中的box-flex弹性盒属性布局
2016/06/09 HTML / CSS
豪华床上用品、床单和浴室必需品:Peacock Alley
2019/09/04 全球购物
.NET初级开发工程师面试题(包括Javascript)
2012/08/22 面试题
如何安装ruby on rails
2014/02/09 面试题
七年级英语教学反思
2016/02/15 职场文书
sass 常用备忘案例详解
2021/09/15 HTML / CSS