React 中的 React.forwardRef 用法简介

在 React 组件中,有时我们可能需要直接操作子组件的 DOM 元素或者访问子组件的 props。React 16.3 版本引入了 React.forwardRef API,它允许我们创建一个高阶组件,将 ref 属性传递给子组件。这在封装组件库或者需要访问底层 DOM 元素时非常有用。

什么是 React.forwardRef

React.forwardRef 是一个函数,它接受一个组件作为参数,并返回一个新的组件。这个新组件可以将 ref 属性转发给其子组件。

如何使用 React.forwardRef

以下是使用 React.forwardRef 的基本步骤:

  1. 导入 React:确保你已经导入了 React。

    import React from 'react';
    
  2. 定义一个组件:创建一个你想要转发 ref 的组件。

    const MyComponent = React.forwardRef((props, ref) => {
      // 使用 props 和 ref 渲染你的组件
      return 
    Hello, World!
    ; });
  3. 使用 ref:在组件中,你可以通过 ref 参数来访问 DOM 元素或者传递给子组件。

    // 组件内部使用 ref
    const MyComponentWithRef = React.forwardRef((props, ref) => {
      return (
        
    ); });
  4. 传递 ref:在使用组件时,你可以像使用任何其他组件一样传递 ref

    // 在组件外部使用 ref
    class App extends React.Component {
      componentDidMount() {
        this.myComponentRef.focus();
      }
    

    render() {
    return <mycomponentwithref ref=“{el” ==“”> this.myComponentRef = el} />;
    }
    }

示例1:封装 input 组件

假设我们想要封装一个 input 组件,并且希望外部能够通过 ref 直接访问到 input 元素。

const InputComponent = React.forwardRef((props, ref) => {
  return (
    
  );
});

// 使用封装的 InputComponent
class Form extends React.Component {
componentDidMount() {
this.inputRef.focus();
}

render() {
return (

<inputcomponent ref=“{input” ==“”> this.inputRef = input} name=“username” />
Submit

);

}
}

在这个例子中,InputComponent 使用 React.forwardRef 创建,它接收 propsref 作为参数,然后将 ref 传递给 input 元素。在 Form 组件中,我们通过 ref 属性将 ref 传递给 InputComponent,并通过 componentDidMount 生命周期方法来获取对 input 元素的引用。

示例2:带图标和提示的按钮组件

下面,让我们来看一个更高级、复杂的例子,其中 React.forwardRef 用于创建一个可定制的、带有图标和浮窗提示的按钮组件。这个组件将允许父级组件通过 ref 访问按钮元素,并且可以根据需要传递一个图标组件和一个提示信息。

1. 定义图标组件和提示组件

首先,我们定义两个简单的组件:一个用于显示图标,一个用于显示提示信息。

const Icon = ({ name }) => ; // 假设我们有一个图标类库
const Tooltip = ({ text }) => {text};

2. 创建可转发 ref 的按钮组件

使用 React.forwardRef 创建一个按钮组件,它接受图标名称和提示信息作为 props,并将 ref 转发给按钮元素。

const CustomButton = React.forwardRef(({ icon, tooltip, children, ...props }, ref) => {
  return (
    
{icon && } {children} {tooltip && }
); });

在这个组件中,我们接收了 icontooltip 作为额外的 props,并且将 ref 传递给了包裹按钮的 div 元素。

3. 使用 useImperativeHandle 控制子组件

假设我们还需要能够通过父组件控制按钮的焦点。我们可以使用 useImperativeHandle 来实现这一点。

const CustomButton = React.forwardRef((props, ref) => {
  const { icon, tooltip, children, ...buttonProps } = props;

const focusButton = () => {
if (ref.current) {
const buttonElement = ref.current.querySelector(‘button’);
buttonElement.focus();
}
};

useImperativeHandle(ref, () => ({
focus: focusButton
}));

return (


{icon && }
<button {…buttonprops}=“”>
{children}

{tooltip && }

);
});

通过 useImperativeHandle,我们给父组件提供了一个 focus 方法,它可以用来聚焦按钮。

4. 使用组件并传递 ref

现在我们可以在父组件中使用 CustomButton 并传递 ref

const App = () => {
  const buttonRef = useRef();

const handleClick = () => {
if (buttonRef.current) {
buttonRef.current.focus(); // 使用通过 useImperativeHandle 提供的方法
}
};

return (



Edit

// … 其他代码

);
};

在这个父组件中,我们创建了一个 buttonRef 并将其传递给 CustomButton。我们还定义了一个 handleClick 方法,当点击事件发生时,它将调用 buttonRef.current.focus() 来聚焦按钮。

这个例子展示了如何使用 React.forwardRef 创建一个高级组件,它不仅转发 ref 给子元素,还利用 useImperativeHandle 提供了额外的方法供父组件使用。这在构建具有复杂交互和可定制性的组件时非常有用。通过这种方式,你可以创建出既强大又灵活的组件,满足各种复杂的应用场景。

注意事项

  • 当你使用 React.forwardRef 时,React 会创建一个特殊的跨组件类型的 ref。这意味着你不能在组件定义中直接使用 ref 属性,因为 ref 必须指向一个 DOM 元素或类组件实例。
  • 确保你传递给 React.forwardRef 的组件是函数组件,因为类组件不能被转发 ref

总结

通过使用 React.forwardRef,你可以创建更灵活的组件,允许父组件直接与其子组件的 DOM 元素或 props 交互,这在构建可复用和可组合的 UI 组件时非常有用。

Tips

使用 ForwardRef 的步骤如下:

  1. 导入 React: 开始之前,确保你已经导入了 React。

    import React from 'react';
    
  2. 创建一个组件工厂: 使用 React.forwardRef 创建一个组件工厂,它接受一个组件作为参数。

    const ForwardRefComponent = React.forwardRef((props, ref) => {
      // ...
    });
    
  3. 定义组件逻辑: 在组件工厂的函数内,定义你的组件逻辑,包括 JSX 结构和任何需要的逻辑。

    const ForwardRefComponent = React.forwardRef((props, ref) => {
      return 
    Some Content
    ; });
  4. 使用 ref 参数: 在组件内部,使用 ref 参数来访问 DOM 元素或者将其传递给子组件。

    const ForwardRefComponent = React.forwardRef((props, ref) => {
      // 将 ref 传递给子组件
      return ;
    });
    
  5. 使用 useImperativeHandle (可选): 如果你需要给父组件提供对子组件实例的控制,可以使用 useImperativeHandle

    const ForwardRefComponent = React.forwardRef((props, ref) => {
      useImperativeHandle(ref, () => ({
        // 定义可以被父组件访问的方法
        customMethod: () => {
          // ...
        }
      }));
      // ...
    });
    
  6. 在父组件中使用 ForwardRefComponent: 在父组件中,你可以通过 ref 属性将引用传递给 ForwardRefComponent

    class ParentComponent extends React.Component {
      componentDidMount() {
        if (this.myRef.current) {
          // 使用 ref 来访问子组件的 DOM 元素或通过 useImperativeHandle 提供的方法
          this.myRef.current.customMethod();
        }
      }
    

    render() {
    return <forwardrefcomponent ref=“{el” ==“”> this.myRef = el} />;
    }
    }


这是一个从 https://juejin.cn/post/7368757335802593318 下的原始话题分离的讨论话题