useImperative
这个 hook 可以自定义一个对象,暴露给父组件。
这个 hook 一般结合 forwardRef
一起使用,实现父组件去调用子组件中的实例属性 or 方法,进而操作子组件的 DOM。
那么使用 useImperative
和 forwardRef
时如何定义类型呢?
我们通过一个简单栗子 🌰 来学习一下 :
先定义一个组件:
const Countdown = React.forwardRef((props,ref) => {
React.useImperativeHandle(ref, ()=>({
start() {
alert('Start');
}
});
return <div>Countdown</div>
});
在父组件中引用该组件:
function App(){
const cntEl:any = React.useRef(null);
React.useEffect(()=>{
if(cntEl.current){ cuntEl.current.start() }
}, []);
return <Countdown ref={cntEl} />
}
很多人会这样写,把 TypeScript 变成 AnyScript,对于 cuntEl.current.start()
还报的 Property 'start' does not exist on type 'never'.
错误 ,直接 // @ts-ignore
把错误干掉。
既然解决不了问题,那就解决提出问题的人。
我们需要做几件限定类型安全的事情:
- 限定 Props 类型。
- 限定在
useImperativeHandle
定义的对象类型。 - 限定
useRef
中的类型。
type CountdownProps = {}
type CountdownHandle = {
start: () => void,
}
// 利用 React 提供的 ForwardRefRenderFunction 去限定 CountdownHandle 和 CountdownProps
const Countdown: React.ForwardRefRenderFunction<CountdownHandle, CountdownProps> = (
props,
forwardedRef,
) => {
React.useImperativeHandle(forwardedRef, ()=>({
start() {
alert('Start');
}
});
return <div>Countdown</div>;
}
export default React.forwardRef(Countdown);
import Countdown, { CountdownHandle } from "./Countdown.tsx";
function App() {
// 限定 useRef 的类型为 CountdownHandle,这样调用 start 不会报错
const countdownEl = React.useRef<CountdownHandle>(null);
React.useEffect(() => {
if (countdownEl.current) {
countdownEl.current.start();
}
}, []);
return <Countdown ref={countdownEl} />;
}
记住,一定要利用好 TypeScript,保障好类型安全,避免 AnyScript。
我是 Pandy,一个喜欢英语的程序猿 👨💻
关注公众号 Yopth,回复「加群」,加入「英文技术翻译互助群」,我们加入一起充电英语 🔌
Reference
[1] https://stackoverflow.com/questions/62210286/declare-type-with-react-useimperativehandle
[2] https://reactjs.org/docs/hooks-reference.html#useimperativehandle