在我正在处理的React代码库中,我有一个自定义钩子,它接受一个RefObject作为参数,并附带一个提供程序用于这样的钩子:
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
}
}
我收到的错误消息:
Argument of type 'ForwardedRef<HTMLDivElement>' is not assignable to parameter of type 'RefObject<HTMLDivElement>'.
Type 'null' is not assignable to type 'RefObject<HTMLDivElement>'.
深入研究这两种类型,我意识到它们真的是不同的:
// from @types/react@17.0.0
interface RefObject<T> {
readonly current: T | null;
}
type ForwardedRef<T> = ((instance: T | null) => void) | MutableRefObject<T | null> | null;
我的问题是:
发布于 2022-07-20 05:55:33
RefObject
interface RefObject<T> {
readonly current: T | null;
}
RefObject
是React.createRef
方法的返回类型。
当调用此方法时,它返回一个对象,其唯一字段.current
设置为null
。不久之后,当render
将ref传递给组件时,React将将.current
设置为对组件的引用。该组件通常是DOM元素(如果传递给HTML元素)或类组件的实例(如果传递给自定义类组件)。
请注意,RefObject
与MutableRefObject<T | null>
非常相似,但.current
是readonly
的例外。制定此类型规范只是为了表明.current
属性是由React在内部管理的,不应该由React应用程序中的代码进行修改。
MutableRefObject
interface MutableRefObject<T> {
current: T;
}
MutableRefObject
是React.useRef
方法的返回类型。在内部,React.useRef
生成一个MutableRefObject
,将其存储到函数组件的状态,并返回对象。
注意,当对象存储到React组件的状态时,修改它们的属性不会触发重呈现(因为Javascript对象是引用类型)。这种情况允许您在没有实例的功能组件中模拟类实例变量。换句话说,您可以将React.useRef
看作一种将变量与功能组件关联起来而不影响组件呈现的方法。
下面是使用实例变量的类组件和使用React.useRef
实现相同目的的功能组件的示例:
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
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
类型。
下面是这篇文章的一个钩子(根据类型记录进行调整),它有助于实现这一点:
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>
参数兼容,例如:
const MyComponent = React.forwardRef<HTMLDivElement>(
function MyComponent(_props, ref) {
const innerRef = useForwardedRef(ref);
useScrollUtils(innerRef);
return <div ref={innerRef}></div>;
}
);
https://stackoverflow.com/questions/73015696
复制相似问题