使用 React.useImperativeHandle() 声明类型

zyn*_*nkn 14 typescript reactjs

function App(){
  const cntEl:any = React.useRef(null);  // I don't know what type should be here.
  React.useEffect(()=>{
    if(cntEl.current){ cuntEl.current.start() }
  }, []);
  return <Countdown ref={cntEl} />
}
const Countdown = React.forwardRef((props,ref) => {
  React.useImperativeHandle(ref, ()=>({
    start() {
      alert('Start');
    }
  });
  return <div>Countdown</div>
});

Run Code Online (Sandbox Code Playgroud)

我尝试使用ref和在父组件中使用子方法React.useImperativeHandle()

它运作良好。

但我不满意,因为const cntEl:any

我相信有很多方法可以避免使用any我不知道的类型。

我只需要一个可以被替换的类型 type any

已编辑

我可以看到(property) React.MutableRefObject<null>.current: null当我悬停在cntEl.current

Huỳ*_*yễn 151

接受的答案过于复杂,您只需要声明一个CountdownHandletype

对我来说,事情会是这样的:

// Countdown.tsx

export type CountdownHandle = {
  start: () => void;
};

type Props = {};

const Countdown = React.forwardRef<CountdownHandle, Props>((props, ref) => {
  React.useImperativeHandle(ref, () => ({
    // start() has type inferrence here
    start() {
      alert('Start');
    },
  }));

  return <div>Countdown</div>;
});
Run Code Online (Sandbox Code Playgroud)
// The component uses the Countdown component

import Countdown, { CountdownHandle } from "./Countdown.tsx";

function App() {
  const countdownEl = React.useRef<CountdownHandle>(null);

  React.useEffect(() => {
    if (countdownEl.current) {
      // start() has type inferrence here as well
      countdownEl.current.start();
    }
  }, []);

  return <Countdown ref={countdownEl} />;
}
Run Code Online (Sandbox Code Playgroud)

  • 这确实更容易理解并且效果很好!谢谢。 (3认同)
  • 如果“Component”也是通用的怎么办?例如 `interface Props&lt;T&gt; { name: T; }` 并且组件可能是 `function Component&lt;T&gt;(props: Props&lt;T&gt;)` (3认同)
  • 同意,这更容易理解,经过大约 18 个月的尝试各种帖子的 hack 后我终于明白了 - 真希望我早点找到这个。是时候重构了... (2认同)
  • 为了清楚起见 - “这有点令人困惑,因为通用参数(ref 然后 props)的顺序与函数参数(props 然后 ref)的顺序相反。” https://www.carlrippon.com/react-forwardref-typescript/ (2认同)

小智 31

我建议您更明确地使用类型定义

例如,使用 React DT,您可以使用ForwardRefRenderFunction而不是FC.

type CountdownProps = {}
    
type CountdownHandle = {
  start: () => void,
}
    
const Countdown: React.ForwardRefRenderFunction<CountdownHandle, CountdownProps> = (
  props,
  forwardedRef,
) => {
  React.useImperativeHandle(forwardedRef, ()=>({
    start() {
      alert('Start');
    }
  });

  return <div>Countdown</div>;
}

export default React.forwardRef(Countdown);
Run Code Online (Sandbox Code Playgroud)

然后使用 React 实用程序ElementRef,TypeScript 可以推断出组件的确切引用类型

const App: React.FC = () => {
  // this will be inferred as `CountdownHandle`
  type CountdownHandle = React.ElementRef<typeof Countdown>;

  const ref = React.useRef<CountdownHandle>(null); // assign null makes it compatible with elements.

  return (
    <Countdown ref={ref} />
  );
};
Run Code Online (Sandbox Code Playgroud)

  • 谢谢。值得注意的是,“RefForwardingComponent”已被弃用,类型定义建议使用“ForwardRefRenderFunction”代替。 (9认同)
  • 您也可以简单地将其声明为 const Countdown = React.forwardRef(props: CountdownProps, ref: React.Ref&lt;CountdownHandle&gt;) =&gt; { ... })` (6认同)
  • 从打字稿中删除 React.FC。[拉](https://github.com/facebook/create-react-app/pull/8177) (2认同)