彻底弄懂react ref

本文详细介绍了React中Refs的用途和三种使用方式:字符串形式、回调函数形式以及React.createRef()。讨论了何时需要使用Refs,如管理焦点、触发动画和集成第三方库,并提供了代码示例。还提到了useRef()在函数组件中的应用以及Ref转发的概念,帮助开发者更好地理解和利用React Refs进行DOM节点和组件实例的访问。
摘要由CSDN通过智能技术生成

react ref

1. 前言

如果有这么一种情况,我们需要在一个组件获取另一个组件或者某个DOM元素,这个时候我们应该怎么做呢。react中的Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。

2. 什么时候需要使用ref

  • 管理焦点,文本选择或媒体播放。(点击之后某个input框聚焦)
  • 触发强制动画。(触发某个DOM的动画)
  • 集成第三方 DOM 库。

3. ref 的使用方式(三种)

  1. 字符串形式ref
    class MyComponent extends React.Component {
      handleClick() {
        // 使用原生的 DOM API 获取焦点
        this.refs.myInput.focus();
      }
      render() {
        //  当组件插入到 DOM 后,ref 属性添加一个组件的引用于到 this.refs
        return (
          <div>
            <input type="text" ref="myInput" />
            <input
              type="button"
              value="点我输入框获取焦点"
              onClick={this.handleClick.bind(this)}
            />
          </div>
        );
      }
    }
     
    ReactDOM.render(
      <MyComponent />,
      document.getElementById('example')
    );
    

    这种方式效率太低了,而且存在一些问题,后续可能被废弃,所以不建议使用

  2. 回调函数的形式
    <script type="text/babel" >
        class Son extends React.Component {
          render() {
            return (
              <div>
                <p ref={this.props.ref4}>我是第一个节点</p>
                <p ref={this.props.ref3}>我是第二个节点</p>
              </div>
            )
          }
        }
    	class Father extends React.Component {
          handleClick() {
            console.log(this.ref1.value);
            console.log(this.ref2.value);
            console.log(this.ref3);
            console.log(this.ref4);
          }
          saveRef2 = (d) => {
            this.ref2 = d;
          }
          render() {
            return (
              <div>
                <input value = "ref1" ref = {d => {this.ref1 = d}} />
    /* 写法一:直接通过内联回调函数的方法进行ref绑定,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。
    */
                <input value = "ref2" ref = {this.saveRef2} />
    /*
    写法二:通过在外部定义函数的方式引入进来,这样就不会每次都创建一个函数实例了,这样就可以只执行一次绑定
    */
                <Son ref3 = {d => this.ref3 = d} />
    /*
    我们可以通过这种方式在父组件来获取子组件某一个节点的值
    */
                <Son ref4 = {d => this.ref4 = d} />
                <button onClick={this.handleClick.bind(this)}>获取所有的refs</button>
              </div>
            )
          }
        }
    	ReactDOM.render(<Father />,document.getElementById('test'))
    </script>
    

    回调函数的方式有两种写法,详细看注释,这种写法对于普通的dom节点和组件都是适合的

    结果:

    image-20210707151937645

  3. React.createRef()

    这种方式也是官方比较推荐的方式。Refs 是使用 React.createRef() 创建的,并通过 ref 属性附加到 React 元素。在构造组件时,通常将 Refs 分配给实例属性,以便可以在整个组件中引用它们。

    class AutoFocusTextInput extends React.Component {
      constructor(props) {
        super(props);
        this.textInput = React.createRef();  
      }
    
      componentDidMount() {
        this.textInput.current.focusTextInput(); 
      }
    
      render() {
        return (
          <CustomTextInput ref={this.textInput} />    
    	);
      }
    }
    

    也就是一般来说会有三个步骤:

    1. 创建一个ref
    2. 将一个ref赋值给某个组件或者dom节点
      class MyComponent extends React.Component {
        constructor(props) {
          super(props);
          this.myRef = React.createRef();  // 创建ref
        }
        render() {
          return <div ref={this.myRef} />;  // 赋值ref
        }
      }
      
    3. 使用这个ref

    当 ref 被传递给 render 中的元素时,对该节点的引用可以在 ref 的 current 属性中被访问。

    const node = this.myRef.current; //current属性是react给我们带上的
    // React 会在组件挂载时给 current 属性传入 DOM 元素,并在组件卸载时传入 null 值。ref 会在 componentDidMount 或 componentDidUpdate 生命周期钩子触发前更新。
    

    ref 的值根据节点的类型而有所不同:

    • ref 属性用于 HTML 元素时,构造函数中使用 React.createRef() 创建的 ref 接收底层 DOM 元素作为其 current 属性。
    • ref 属性用于自定义 class 组件时,ref 对象接收组件的挂载实例作为其 current 属性。
    • 你不能在函数组件上使用 ref 属性,因为他们没有实例。

    请注意:我们的ref一定会是某个实例,因为函数没有实例,所以不能再函数组件上使用ref

    function MyFunctionComponent() {
      return <input />;
    }
    
    class Parent extends React.Component {
      constructor(props) {
        super(props);
        this.textInput = React.createRef();
      }
      render() {
        // 这个不会生效,因为函数组件没有实例
        return (
          <MyFunctionComponent ref={this.textInput} />
        );
      }
    }
    
  4. useRef()

    虽然我们不能直接在函数组件上使用ref,但是我们可以在函数内部使用useRef,因为函数内部可以有DOM节点或者其它的class组件供我们访问,但是这个时候我们不能使用React.createRef(),而是需要使用useRef这个钩子函数。

    function CustomTextInput(props) {
      // 这里必须声明 textInput,这样 ref 才可以引用它
      const textInput = useRef(null); // 初始值设置为null,后面挂载后再赋值
    
      function handleClick() {
        textInput.current.focus();
      }
    
      return (
        <div>
          <input
            type="text"
            ref={textInput} />
          <input
            type="button"
            value="Focus the text input"
            onClick={handleClick}
          />
        </div>
      );
    }
    
  5. ref转发(React.forwardRef)

    Ref 转发是一项将 ref 自动地通过组件传递到其一子组件的技巧。对于大多数应用中的组件来说,这通常不是必需的。但其对某些组件,尤其是可重用的组件库是很有用的。

    考虑这个渲染原生 DOM 元素 buttonFancyButton 组件:

    function FancyButton(props) {
      return (
        <button className="FancyButton">
          {props.children}
        </button>
      );
    }
    

    React 组件隐藏其实现细节,包括其渲染结果。其他使用 FancyButton 的组件通常不需要获取内部的 DOM 元素 buttonref。这很好,因为这防止组件过度依赖其他组件的 DOM 结构。

    虽然这种封装对类似 FeedStoryComment 这样的应用级组件是理想的,但其对 FancyButtonMyTextInput 这样的高可复用“叶”组件来说可能是不方便的。这些组件倾向于在整个应用中以一种类似常规 DOM buttoninput 的方式被使用,并且访问其 DOM 节点对管理焦点,选中或动画来说是不可避免的。

    Ref 转发是一个可选特性,其允许某些组件接收 ref,并将其向下传递(换句话说,“转发”它)给子组件。

    在下面的示例中,FancyButton 使用 React.forwardRef 来获取传递给它的 ref,然后转发到它渲染的 DOM button

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

    这样,使用 FancyButton 的组件可以获取底层 DOM 节点 button 的 ref ,并在必要时访问,就像其直接使用 DOM button 一样。

    以下是对上述示例发生情况的逐步解释:

    1. 我们通过调用 React.createRef 创建了一个 React ref 并将其赋值给 ref 变量。
    2. 我们通过指定 ref 为 JSX 属性,将其向下传递给 <FancyButton ref={ref}>
    3. React 传递 refforwardRef 内函数 (props, ref) => ...,作为其第二个参数。
    4. 我们向下转发该 ref 参数到 <button ref={ref}>,将其指定为 JSX 属性。
    5. 当 ref 挂载完成,ref.current 将指向 <button> DOM 节点。

    注意

    第二个参数 ref 只在使用 React.forwardRef 定义组件时存在。常规函数和 class 组件不接收 ref 参数,且 props 中也不存在 ref

    Ref 转发不仅限于 DOM 组件,你也可以转发 refs 到 class 组件实例中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值