• 一次性搞定React的Ref功能


    React的Ref功能

    1.String Ref

    String Ref是个过时的API。因为String类型的Ref存在一些问题,将在未来的某个版本中被遗弃,不建议使用。

    使用方式:this.refs.XXX获取DOM元素节点:

    获取普通标签:

    import React, { Component } from 'react';
    class App extends Component {
        componentDidMount() {
            console.log('this.refs.XXX');
            console.log(this.refs.h1Ref);
        }
        render() {
            return <h1 ref='h1Ref'>Hello World!</h1>
        }
    }
    export default App;
    

    复制代码打印结果:

    this.refs.xxx
    <h1>Hello World!</h1>
    

    获取react组件:此时可以调用组件上的方法

    import React, { Component } from 'react';
    class App extends Component {
        componentDidMount() {
            console.log(this.refs.childRef);
            this.refs.childRef.handleLog(); // Child Component    }
        render() {
            return (
                <div>
                    <h1>Hello World!</h1>
                    <Child ref='childRef' count='1' />
                </div>
            )
        }
    }
    class Child extends Component {
        handleLog = () => {
            console.log('Child Component');
        }
        render() {
            const { count } = this.props;
            return <h2>count: { count }</h2>
        }
    }
    export default App
    

    // 结果
    组件的引用,包括上面挂载的函数和组件props等

    2.Callback Ref

    Callback Ref能助你更精细地控制何时 refs

    被设置和解除,传递一个函数。这个函数中接受 React 组件实例或 HTML DOM 元素作为参数。

    React 将在组件挂载时,会调用 ref 回调函数并传入 DOM 元素,当卸载时调用它并传入 null。在 componentDidMount 或 componentDidUpdate 触发前,React 会保证 refs 一定是最新的。

    使用方式:ref={element => (this.eleref = element)}

    获取DOM元素节点:

    import React, { Component } from "react";
    class App extends Component {
      componentDidMount() {
        console.log("Callback Ref");
        console.log(this.h1Ref);
      }
      render() {
        return (
          <div>
            <h1 ref={element => (this.h1Ref = element)}>Hello World!</h1>
          </div>
        );
      }
    }
    export default App;
    
    // 打印结果
     Callback Ref
     <h1>Hello World!</h1>
    
    

    获取子组件实例:【同上可调用方法】

    import React, { Component } from "react";
    class App extends Component {
      componentDidMount() {
        console.log("Callback Ref");
        console.log(this.childRef);
        this.childRef.handleLog();
      }
      render() {
        return (
          <div>
            <h1>Hello World!</h1>
            <Child ref={component => (this.childRef = component)} count="1" />
          </div>
        );
      }
    }
    class Child extends Component {
      handleLog = () => {
        console.log("Child Component");
      };
      render() {
        const { props } = this;
        return <h1>count: {props.count}</h1>;
      }
    }
    export default App;
    

    3.Create Ref

    该功能是React16.3中发布的,并且在类组件中推荐使用

    使用React.createRef()创建Refs,通过ref附加到组件中,对该节点的引用通过ref的current属性访问

    React在组件挂载时给current传入DOM元素,并在组件卸载时传入null,ref会在生命周期函数前更新完成

    使用方法和前两种类似,这里就不一一列举了

    import React, { Component, createRef} from "react";
    class App extends Component {
      constructor(props) {
        super(props);
        this.h1Ref = createRef();
      }
      componentDidMount() {
        console.log("React.createRef()");
        console.log(this.h1Ref.current);
      }
      render() {
        return <h1 ref={this.h1Ref}>Hello World!</h1>;
      }
    }
    export default App;
    
    

    4.useRef

    当在函数组件中使用前三种ref,会抛出以下错误:Uncaught Invariant Violation: Function components cannot have refs. Did you mean to use React.forwardRef()?

    原因:函数组件和class组件根本区别是,函数组件没有实例,所以无法使用实例对象,取而代之的为useRef,或使用forwardRef

    作用:

    获取DOM节点
    获取组件实例
    渲染周期之间共享数据存储(state修改会触发重新渲染,所以不能跨周期共享)
    使用方法:与createRef类似,挂在current上

    import React, { useEffect, useRef } from 'react';
    function App() {
      const h1Ref = useRef();
      useEffect(() => {
        console.log('useRef')
        console.log(h1Ref.current)
      }, [])
      return <h1 ref={h1Ref}>Hello World!</h1>
    }
    export default App;
    
    

    5 不同渲染周期之间的数据共享

    该情况主要解决一类问题:类组件中,函数组件中的属性无法跨渲染周期进行数据共享,因为每次保存都会刷新数据重新渲染

    示例程序:(其中的timer无法跨渲染周期,每次执行App函数,其都会被重置)

    import React, { useState, useEffect, useRef } from "react";
    function App() {
      const [count, setCount] = useState(0);
      // 把定时器设置成全局变量使用useRef挂载到current上
      const timer = useRef();
      // 首次加载useEffect方法执行一次设置定时器
      useEffect(() => {
        timer.current = setInterval(() => {
          setCount(count => count + 1); // 此处必须传入函数,否则拿到的都是组将挂载时的初值!!
        }, 1000);
      }, []);
      // count每次更新都会执行这个副作用,当count > 5时,清除定时器
      useEffect(() => {
        if (count > 5) {
          clearInterval(timer.current);
        }
      });
      return <h1>count: {count}</h1>;
    }
    export default App;
    
    

    6 各方式使用总结

    ref方式定义获取元素引用执行挂载在元素内的函数备注
    string ref标签内ref=‘xxx’this.refs.xxxthis.refs.xxx.handle()已过时
    callback ref标签内ref={(element)=>{this.xxx=element}}this.xxxthis.xxx.handle()精准控制ref
    create ref构造中this.xxx=createRef() 标签中ref={this.xxx}this.xxx.currentthis.xxx.current.handle()推荐使用
    useRef()函数组件中const 函数标签中xxx=useRef() ref={xxx}xxx.currentxxx.current.handle()函数组件中的ref替代方法

    参考文档

    7 Ref转发

    有时候需要传递ref给子组件,需要用到React.forwardRef函数,因为常规函数组件和 class 组件不接收 ref 参数,且 props 中也不存在 ref

    const FancyButton = React.forwardRef((props, ref) => (
      <button ref={ref} className="FancyButton">
        {props.children}
      </button>
    ));
    
    // 你可以直接获取 DOM button 的 ref:
    const ref = React.createRef();
    // 此时的ref即React.forwardRef的参数之一
    <FancyButton ref={ref}>Click me!</FancyButton>;
    

    这个例子展示了,将ref转发到了最终的button标签上,其实Ref 转发不仅限于 DOM 组件,你也可以转发 refs到 class组件实例中。

    HOC Ref转发

    function logProps(Component) {
      class LogProps extends React.Component {
        componentDidUpdate(prevProps) {
          console.log('old props:', prevProps);
          console.log('new props:', this.props);
        }
    
        render() {
          const {forwardedRef, ...rest} = this.props;
    
          // 将自定义的 prop 属性 “forwardedRef” 定义为 ref
          return <Component ref={forwardedRef} {...rest} />;
        }
      }
    
      // 注意 React.forwardRef 回调的第二个参数 “ref”。
      // 我们可以将其作为常规 prop 属性传递给 LogProps,例如 “forwardedRef”
      // 然后它就可以被挂载到被 LogProps 包裹的子组件上。
      return React.forwardRef((props, ref) => {
        return <LogProps {...props} forwardedRef={ref} />;
      });
    }
    

    这时候logProps如何调用,怎么同时把Component和ref传进去
    猜测:

    const Test = logProps(<button>123</button>)
    const ref = React.createRef()
    <Test ref={ref}/>
    
  • 相关阅读:
    QB学堂济南游记
    区间质数查询 luoguP1865
    基础数据结构 ①(栈|队列|链表)
    图论算法->最短路
    小谈记忆化搜索
    Hibernate其它API
    Hibernate中Session与本地线程绑定
    Hibernate事务代码规范写法
    实体类对象的三种状态
    对实体类的CRUD操作
  • 原文地址:https://www.cnblogs.com/tommymarc/p/16147773.html
Copyright © 2020-2023  润新知