React中的forwardRef

315 阅读2分钟

对于ref转发,官网中有这样的描述

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

让我们简单的翻译一下:React.forwardRef 是 React 中的一个高级 API,它用于在组件之间传递 ref。通常情况下,当你创建一个组件时,ref 是无法传递给组件的子元素的,但是使用 React.forwardRef 可以解决这个问题。

React.forwardRef(render)的返回值是react组件,接收的参数是一个render函数,函数签名为render(props, ref),第二个参数将其接受的 ref 属性转发到render返回的组件中。

这项技术在以下两种场景中特别有用:

  • 封装第三方组件:如果你在自己的组件中使用了第三方的组件,并且希望通过ref控制第三方组件,可以使用React.forwardRef;
  • 高阶组件(HOC):在创建高阶组件的时候,React.forwardRef 可以用来保留原始组件的ref

场景一:封装第三方的组件

假设你使用了一个第三方的输入框组件,但是你想在父组件中控制这个输入框的焦点,使用React.forwardRef 可以帮助你将ref传递给第三方组件。

import React, { forwardRef, useEffect, useRef } from 'react';
import ThirdPartyInput from 'third-party-input';

const CustomInput = forwardRef((props, ref) => {
  const inputRef = useRef(null);

  useEffect(() => {
    // 这里可以添加一些额外的逻辑
    // 例如:聚焦输入框
    if (ref) {
      ref.current = {
        focus: () => {
          if (inputRef.current) {
            inputRef.current.focus();
          }
        }
      };
    }
  }, [ref]);

  return <ThirdPartyInput ref={inputRef} {...props} />;
});

// 在父组件中使用 CustomInput
const ParentComponent = () => {
  const customInputRef = useRef(null);

  const handleButtonClick = () => {
    // 在按钮点击时聚焦输入框
    if (customInputRef.current) {
      customInputRef.current.focus();
    }
  };

  return (
    <div>
      <CustomInput ref={customInputRef} />
      <button onClick={handleButtonClick}>Focus Input</button></div>
  );
};

场景二:高阶组件(HOC)

在高阶组件的场景中,你可以使用 React.forwardRef来确保传递给高阶组件的 ref能够正确地传递给原始组件。

import React, { forwardRef } from 'react';

// 高阶组件
const withLogging = (WrappedComponent) => {
  const WithLogging = forwardRef((props, ref) => {
    // 在这里添加一些日志记录的逻辑
    console.log('Component is rendered with props:', props);

    // 将 ref 传递给原始组件
    return <WrappedComponent {...props} ref={ref} />;
  });

  return WithLogging;
};

// 创建原始组件
const MyComponent = ({ label }, ref) => {
  return (
    <div ref={ref}>
      <p>{label}</p>
    </div>
  );
};

// 使用高阶组件
const MyComponentWithLogging = withLogging(MyComponent);

// 在父组件中使用 MyComponentWithLogging
const ParentComponent = () => {
  const myComponentRef = React.createRef();

  return (
    <div>
      <MyComponentWithLogging label="Hello" ref={myComponentRef} />
      {/* 在这里可以通过 myComponentRef 控制 MyComponentWithLogging 组件 */}
    </div>
  );
};