首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >React的ForwardedRef和RefObject有什么区别?

React的ForwardedRef和RefObject有什么区别?
EN

Stack Overflow用户
提问于 2022-07-17 22:10:55
回答 1查看 639关注 0票数 8

在我正在处理的React代码库中,我有一个自定义钩子,它接受一个RefObject作为参数,并附带一个提供程序用于这样的钩子:

代码语言:javascript
运行
复制
export const ScrollUtilsProvider = React.forwardRef<HTMLDivElement, ScrollUtilsProviderProps>(
  (props, ref) => {
    const scrollUtils = useScrollUtils(ref) // issue happens on this line
    return <div ref={ref}><ScrollUtilsContext.Provider value={scrollUtils}>{props.children}</ScrollUtilsContext.Provider></div>
  },
)

export const useScrollUtils = <T extends Element>(ref: RefObject<T>) => {
  return {
    // some cool functions w/ the passed ref
  }
}

我收到的错误消息:

代码语言:javascript
运行
复制
Argument of type 'ForwardedRef<HTMLDivElement>' is not assignable to parameter of type 'RefObject<HTMLDivElement>'.
  Type 'null' is not assignable to type 'RefObject<HTMLDivElement>'.

深入研究这两种类型,我意识到它们真的是不同的:

代码语言:javascript
运行
复制
// from @types/react@17.0.0
interface RefObject<T> {
  readonly current: T | null;
}
type ForwardedRef<T> = ((instance: T | null) => void) | MutableRefObject<T | null> | null;

我的问题是:

  • 为什么这些类型如此不同?
  • 有什么方法可以使用ForwardedRef作为RefObject吗?
  • 如果没有,有什么让ForwardedRefs如此特别的东西吗?
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-07-20 05:55:33

RefObject

代码语言:javascript
运行
复制
interface RefObject<T> {
    readonly current: T | null;
}

RefObjectReact.createRef方法的返回类型。

当调用此方法时,它返回一个对象,其唯一字段.current设置为null。不久之后,当render将ref传递给组件时,React将将.current设置为对组件的引用。该组件通常是DOM元素(如果传递给HTML元素)或类组件的实例(如果传递给自定义类组件)。

请注意,RefObjectMutableRefObject<T | null>非常相似,但.currentreadonly的例外。制定此类型规范只是为了表明.current属性是由React在内部管理的,不应该由React应用程序中的代码进行修改。

MutableRefObject

代码语言:javascript
运行
复制
interface MutableRefObject<T> {
    current: T;
}

MutableRefObjectReact.useRef方法的返回类型。在内部,React.useRef生成一个MutableRefObject,将其存储到函数组件的状态,并返回对象。

注意,当对象存储到React组件的状态时,修改它们的属性不会触发重呈现(因为Javascript对象是引用类型)。这种情况允许您在没有实例的功能组件中模拟类实例变量。换句话说,您可以将React.useRef看作一种将变量与功能组件关联起来而不影响组件呈现的方法。

下面是使用实例变量的类组件和使用React.useRef实现相同目的的功能组件的示例:

代码语言:javascript
运行
复制
class ClassTimer extends React.Component {
    interval: NodeJS.Timer | null = null;

    componentDidMount() {
        this.interval = setInterval(() => { /* ... */ });
    }

    componentWillUnmount() {
        if (!this.interval) return;
        clearInterval(this.interval);
    }

    /* ... */
}

function FunctionalTimer() {
    const intervalRef = React.useRef<NodeJS.Timer>(null);

    React.useEffect(() => {
        intervalRef.current = setInterval(() => { /* ... */ });
        return () => {
            if (!intervalRef.current) return;
            clearInterval(intervalRef.current);
        };
    }, []);

    /* ... */
}

ForwardedRef

代码语言:javascript
运行
复制
type ForwardedRef<T> = 
    | ((instance: T | null) => void)
    | MutableRefObject<T | null> 
    | null;

ForwardedRef是使用React.forwardRef向功能组件传递ref的类型。

这里的主要思想是父组件可以向下传递一个引用到子组件。例如,MyForm可以向MyTextInput转发一个ref,允许前者访问后者呈现的HTMLInputElement.value

打破工会类型:

  • MutableObject<T | null> -转发的引用是用React.useRef创建的。
  • ((instance: T | null) => void) -转发的裁判是一个回调参考
  • null -没有转发参考资料。

在子组件中使用ForwardedRef

当子组件接收到ForwardedRef时,通常是向父组件公开引用。但是,有时子组件可能需要使用ref本身。在这种情况下,您可以使用钩子来协调上面列出的每个ForwardedRef类型。

下面是这篇文章的一个钩子(根据类型记录进行调整),它有助于实现这一点:

代码语言:javascript
运行
复制
function useForwardedRef<T>(ref: React.ForwardedRef<T>) {
    const innerRef = React.useRef<T>(null);

    React.useEffect(() => {
        if (!ref) return;
        if (typeof ref === 'function') {
            ref(innerRef.current);
        } else {
            ref.current = innerRef.current;
        }
    });

    return innerRef;
}

这个钩子背后的想法是,组件可以创建自己的ref,它可以使用它,而不管父节点是否转发了一个ref。钩子有助于确保所有转发的ref的.current属性与内部的属性保持同步。

这个钩子的返回类型是MutableRefObject<T>,它应该与useScrollUtils代码片段中的RefObject<T>参数兼容,例如:

代码语言:javascript
运行
复制
const MyComponent = React.forwardRef<HTMLDivElement>(
    function MyComponent(_props, ref) {
        const innerRef = useForwardedRef(ref);

        useScrollUtils(innerRef);

        return <div ref={innerRef}></div>;
    }
);
票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73015696

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档