React - useRef 与 TypeScript 和功能组件

问题描述 投票:0回答:3

我正在尝试使用

useRef
从父组件调用子组件方法。

将来,

SayHi
方法将更新子组件中的钩子状态。不幸的是,我有无法处理的错误。

线路:

ref.current.SayHi();

类型“ForwardRefExoticComponent<{ name: string; } & RefAttributes<{ SayHi: () => void”上不存在属性“SayHi”; }>>'。

线路:

<Child name="Adam" ref={ref}/>

输入“RefObjectvoid”; }>>>' 不可分配给类型 '((instance: { SayHi: () => void; } | null) => void) | RefObject<{ SayHi: () => 无效; }> |空 |不明确的'。 输入 'RefObject void; }>>>' 不可分配给类型 'RefObject<{ SayHi: () => void; }>'。 类型“ForwardRefExoticComponent<{ name: string; } & RefAttributes<{ SayHi: () => void”中缺少属性“SayHi”; }>>' 但在类型 '{ SayHi: () => void; 中是必需的}'.

完整

test.tsx
文件:

import React, { useRef, forwardRef, useImperativeHandle, Ref } from 'react'

const Parent = () => {
  const ref = useRef<typeof Child>(null);
  const onButtonClick = () => {
    if (ref.current) {
      ref.current.SayHi();
    }
  };
  return (
    <div>
      <Child name="Adam" ref={ref}/>
      <button onClick={onButtonClick}>Log console</button>
    </div>
  );
}

const Child = forwardRef((props: {name: string}, ref: Ref<{SayHi: () => void}>)=> {
  const {name} = props;
  useImperativeHandle(ref, () => ({ SayHi }));
  
  function SayHi() { console.log("Hello " + name); }
  
  return <div>{name}</div>;
});

我在这个话题上深深寻求帮助。

reactjs typescript ref tsx react-functional-component
3个回答
84
投票

您需要在其他地方提取引用类型:

interface RefObject {
  SayHi: () => void
}

然后在两个地方引用即可

const Child = forwardRef((props: {name: string}, ref: Ref<RefObject>)=> {
  const {name} = props;  
  useImperativeHandle(ref, () => ({ SayHi }));
  function SayHi() { console.log("Hello " + name); }

  return <div>{name}</div>;
});
const Parent = () => {
    const ref = useRef<RefObject>(null);
    const onButtonClick = () => {
      if (ref.current) {
        ref.current.SayHi();
      }
    };
    return (
      <div>
        <Child name="Adam" ref={ref}/>
        <button onClick={onButtonClick}>Log console</button>
      </div>
    );
}

5
投票

只需将您的

ref
的声明替换为这个
const ref = useRef<{ SayHi: () => void }>(null);


0
投票

useRef<typeof SomeForwardRefComponent>
的问题是,它认为ref.current将获得
forwardRef
返回的对象类型。然后它期望诸如
ref.current.displayName
而不是
ref.current.focus()
等属性。

您可以使用React的

ElementRef
类型工具提取传递给forwardRef的ref参数的类型,例如
useRef<ElementRef<typeof Child>>

import React, { type ElementRef, type Ref, forwardRef, useRef } from 'react'

const Child = forwardRef<SomeElementType, Props>((props, ref) => (
  /* ...render something */
)
// ...or (same thing but with slightly different syntax where you specify ref type):
const Child = forwardRef((props: Props, ref: Ref<SomeElementType>) => (
  /* ...render something */

)

const Parent = () => {
  // ref here has the same type as if you did useRef<SomeElementType>
  const ref = useRef<ElementRef<typeof Child>>(null)
  return (
    <Child ref={ref} ... />
  )
}

这可行,但是

ElementRef<typeof Child>
有点啰嗦。它基本上只是作为类型参数传递给
forwardRef
的任何内容的复杂别名。

如果您从不受控制的库导入

forwardRef
ed 组件,这可能是最好的解决方案,但如果您控制父级和子级的代码,则最好只定义 ref 类型然后导出和导入它。例如:

// In `child.tsx`
import React, { forwardRef } from 'react'

export type ChildRefType = SomeElementType
export type ChildProps = { ... }

export const Child = forwardRef<ChildRefType, ChildProps>((props, ref) => (
  /* render something */
))
// In `parent.tsx`
import React, { useRef } from 'react'
import { Child, ChildRefType } from './child'

export const Parent = () => {
  // ref here has the same type as if you did useRef<SomeElementType>
  const ref = useRef<ChildRefType>(null)
  return (
    <Child ref={ref} ... />
  )
}
© www.soinside.com 2019 - 2024. All rights reserved.