封装个组件怎么连ref都拿不到?React你礼貌吗?

React.forwardRef

在 React 的组件化开发中,forward是一个非常重要且实用的 API,它主要用于解决高阶组件(HOC)或其他组件包装方式中,原始组件的属性和引用传递问题。

一、React.forwardRef的基本概念

在正常情况下,ref属性不能像普通属性一样通过组件传递,因为ref并不是组件 props 的一部分。而React.forwardRef则打破了这一限制,让组件可以将ref转发给内部的某个子元素。React.forwardRef是 React 提供的一个函数,它允许组件接收并向子组件传递ref。

其基本语法如下:

jsx 复制代码
const ForwardedComponent = React.forwardRef((props, ref) => {
  return <div ref={ref} {...props} />;
});

const App(){
    const myRef = useRef(null)
    return (
        <ForwardedComponent ref={myRef}/>
    )
}

这里,使用forwardRef接收一个渲染函数,该函数接收props和ref两个参数,并返回一个 React 元素。通过这种方式,当外部组件使用ForwardedComponent并传递ref时,这个ref会被传递到内部的div元素上。

二、React.forwardRef的实现原理

从本质上来说,React.forwardRef创建了一个特殊的组件类型。当 React 渲染这种组件时,会将ref作为第二个参数传递给渲染函数,而不是像普通组件那样忽略ref。

在 React 内部,组件会被分为不同的类型,forwardRef创建的组件会被标记为一种特殊的类型。当处理这种类型的组件时,React 会知道要将ref传递给渲染函数,而不是将其作为普通的ref附加到组件实例上(对于类组件)或忽略(对于函数组件)。

三、React.forwardRef的使用场景

  1. 高阶组件(HOC)中转发 ref

    高阶组件是 React 中复用组件逻辑的一种方式,但它可能会导致ref传递出现问题。例如,当我们创建一个 HOC 来包装组件时,外部传递给 HOC 的ref会指向 HOC 本身而不是被包装的原始组件。这时,使用 React.forwardRef 可以将 ref 转发到原始组件上。

    jsx 复制代码
    function withLogging(MyComponent) {
      const ComponentWithLogging = React.forwardRef((props, ref) => {
        console.log('Component rendered');
        return <MyComponent ref={ref} {...props} />;
      });
      return ComponentWithLogging;
    }
    
    const MyComponent = (props) => <div {...props} />;
    // 使用withLogging 包裹 MyComponent 形成 MyComponentWithLogging
    const MyComponentWithLogging = withLogging(MyComponent);
    
    // 使用时,ref会指向MyComponent内部的元素
    const ref = React.createRef();
    <MyComponentWithLogging ref={ref} />;
  2. 在 UI 组件库中使用

    在开发 UI 组件库时,很多组件是由多个基础元素组合而成的。为了让使用者能够方便地获取到组件内部的某个关键元素(如输入框、按钮等),组件库的开发者可以使用 React.forwardRef 将 ref 转发到对应的内部元素上。

    例如,一个自定义的输入框组件:

    jsx 复制代码
    const CustomInput = React.forwardRef((props, ref) => {
      return (
        <div className="custom-input">
          <input ref={ref} {...props} />
        </div>
      );
    });

    使用者在使用CustomInput时,可以像使用普通input一样获取ref,从而操作输入框元素。

  3. 与其他 React 特性结合使用

    React.forwardRef 还可以与其他 React 特性(如useImperativeHandle)结合使用,自定义暴露给父组件的实例值。useImperativeHandle可以让组件指定通过ref 暴露给父组件的方法和属性,而React.forwardRef则负责将ref传递进来。

    jsx 复制代码
    const CustomComponent = React.forwardRef((props, ref) => {
      const [value, setValue] = React.useState('');
    
      React.useImperativeHandle(ref, () => ({
        focus: () => {
          // 执行聚焦操作
        },
        getValue: () => value
      }));
    
      return <input value={value} onChange={(e) => setValue(e.target.value)} />;
    });

    这样,父组件通过ref可以调用focus方法和getValue方法,而不是直接获取input元素。

四、使用React.forwardRef的注意事项

  1. 只在函数组件中使用

    React.forwardRef的渲染函数是一个函数组件,它不能是类组件。如果需要在类组件中使用类似的功能,可以通过其他方式(如将ref作为普通 props 传递,命名为innerRef等)来实现。

  2. 避免过度使用

    虽然React.forwardRef很有用,但也不要过度使用。在大多数情况下,通过 props 传递数据和回调函数已经足够满足需求。只有当确实需要获取组件内部元素的引用时,才考虑使用React.forwardRef。

  3. 类型定义(TypeScript)

    在使用 TypeScript 开发时,需要为forwardRef创建的组件正确定义类型,以确保类型安全。可以使用ForwardedRef和ComponentPropsWithoutRef等类型工具来辅助定义。

    jsx 复制代码
    import React, { forwardRef, ForwardedRef } from 'react';
    
    interface CustomInputProps {
      label: string;
    }
    
    const CustomInput = forwardRef((props: CustomInputProps, ref: ForwardedRef<HTMLInputElement>) => {
      return (
        <div>
          <label>{props.label}</label>
          <input ref={ref} />
        </div>
      );
    });

五、总结

React.forwardRef是 React 中一个非常实用的 API,它解决了ref在组件层级中传递的问题,使得高阶组件、UI 组件库等场景下的开发更加便捷。通过理解其基本概念、使用场景和工作原理,并注意使用过程中的一些细节,可以更好地发挥它的作用,提高 React 应用的开发效率和代码质量。

相关推荐
zengyuhan50320 分钟前
Windows BLE 开发指南(Rust windows-rs)
前端·rust
醉方休24 分钟前
Webpack loader 的执行机制
前端·webpack·rust
前端老宋Running32 分钟前
一次从“卡顿地狱”到“丝般顺滑”的 React 搜索优化实战
前端·react.js·掘金日报
隔壁的大叔32 分钟前
如何自己构建一个Markdown增量渲染器
前端·javascript
用户44455436542635 分钟前
Android的自定义View
前端
WILLF35 分钟前
HTML iframe 标签
前端·javascript
枫,为落叶1 小时前
Axios使用教程(一)
前端
小章鱼学前端1 小时前
2025 年最新 Fabric.js 实战:一个完整可上线的图片选区标注组件(含全部源码).
前端·vue.js
ohyeah1 小时前
JavaScript 词法作用域、作用域链与闭包:从代码看机制
前端·javascript
流星稍逝1 小时前
手搓一个简简单单进度条
前端