React操作DOM之forwardRef问题怎么解决

作者:有用网 阅读量:176 发布时间:2024-01-02
关键字 react

本篇内容主要讲解“React操作DOM之forwardRef问题怎么解决”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“React操作DOM之forwardRef问题怎么解决”吧!

    React操作DOM之forwardRef

    React操作DOM有几种方式,传入字符串,传入一个对象(react推荐的方式),传入一个函数,今天就讲一下使用react封装过的高阶组件forwardRef来操作DOM

    首先导入

    import React, { PureComponent,createRef,forwardRef } from 'react'

    然后const一个函数组件,将它作为App的子组件

    const Profile = forwardRef(function (props,ref){
      return <h3 ref={ref}>Profile</h3>
    })

    定义App组件

    export default class App extends PureComponent {
      constructor(props){
        super(props);
        this.profileRef = createRef()
      }
      render() {
        return (
          <div>
            <Profile ref={this.profileRef} name={'lsh'}/>
            <button onClick={e=>this.printRef()}>点击</button>
          </div>
        )
      }
      printRef(){
        console.log(this.profileRef.current)
      }
    }

    当我们点击按钮时候

    React操作DOM之forwardRef问题怎么解决

    用这个的好处是什么?因为我们之前操作dom,函数式组件是不行的,因为它没有实例,用这个高阶组件就能完美解决这个问题

    React forwardRef使用方法

    作用与注意点

    • 传递ref,把自身的ref绑定到其他地方(e.g. 你把文件交给总裁秘书,总裁秘书把文件交给总裁)

    • ref 和 key 有点特殊,不会作为props参数向下传递,this.props拿不到ref对象

    • 函数组件是没有实例的,可以用useImperativeHandle实现部分功能

    • 高阶组件需做特殊处理

    React操作DOM之forwardRef问题怎么解决

    父 -> 子 -> 子(Dom)

    React操作DOM之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操作DOM之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会绑定到外层的高阶组件上

    • 高阶组件可以嵌套多层,e.g. Hoc1(Hoc2(Hoc3(Content)))

    所以为了把ref传递给最里面的组件,有两种方法

    • 在最外层用 forwardRef 对 ref 对象进行处理,ref -> ref -> props.key = ref

    • 不用 ref,用自定义props承载 ref 对象,props.key = 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 = withRouter(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);
    
    * */

    #发表评论
    提交评论