TypeScript-React Hooks 使用指南:从基础到实践

TypeScript-React Hooks 使用指南:从基础到实践

react Cheatsheets for experienced React developers getting started with TypeScript react 项目地址: https://gitcode.com/gh_mirrors/rea/react

前言

在 React 与 TypeScript 的结合使用中,Hooks 的类型定义和使用方式往往会让开发者感到困惑。本文将深入解析如何在 TypeScript 中正确使用各种 React Hooks,帮助开发者避免常见类型错误,提高代码质量。

useState:状态管理的类型安全

useState 是 React 中最基础的 Hook,TypeScript 能够很好地推断简单值的类型:

const [state, setState] = useState(false);
// state 被推断为 boolean 类型
// setState 只能接受 boolean 参数

复杂状态类型处理

当状态初始值为 null 或 undefined 时,我们需要显式声明类型:

const [user, setUser] = useState<User | null>(null);

// 后续使用
setUser(newUser); // 必须传入 User 类型或 null

类型断言的使用

如果确定状态很快会被初始化且后续始终有值,可以使用类型断言:

const [user, setUser] = useState<User>({} as User);

⚠️ 注意:这种方式会"欺骗"TypeScript 编译器,认为空对象 {}User 类型。必须确保后续确实设置了 user 状态,否则可能导致运行时错误。

useCallback:记忆化函数的类型定义

useCallback 的类型定义与普通函数类似:

const memoizedCallback = useCallback(
  (param1: string, param2: number) => {
    return { ok: true };
  },
  [...],
);

React 18 的类型变化

在 React 18 中,useCallback 的类型签名发生了变化:

  • React <18: (callback: T, deps: DependencyList) => T(参数默认为 any[]
  • React ≥18: (callback: Function, deps: DependencyList) => T

这意味着在 React 18 中,以下代码会报错:

useCallback((e) => {}, []); // 错误:参数 'e' 隐式具有 'any' 类型

解决方案是显式声明参数类型:

useCallback((e: any) => {}, []); // 显式声明 any 类型

useReducer:高级状态管理的类型方案

对于复杂的状态逻辑,useReducer 配合 Discriminated Unions(可辨识联合)是绝佳选择:

type ACTIONTYPE =
  | { type: "increment"; payload: number }
  | { type: "decrement"; payload: string };

function reducer(state: StateType, action: ACTIONTYPE) {
  switch (action.type) {
    case "increment":
      return { count: state.count + action.payload };
    case "decrement":
      return { count: state.count - Number(action.payload) };
    default:
      throw new Error();
  }
}

关键点:

  1. 明确定义 action 类型
  2. 为 reducer 函数指定返回类型
  3. 使用联合类型处理不同的 action

useEffect/useLayoutEffect:副作用处理的类型注意事项

这两个 Hook 主要用于执行副作用操作,通常不需要特别处理类型。但需要注意:

  1. 不要返回除函数或 undefined 外的任何值
  2. 箭头函数中隐式返回可能导致问题

错误示例:

useEffect(
  () => setTimeout(() => {}, timerMs), // 隐式返回 number(setTimeout 的返回值)
  [timerMs]
);

正确写法:

useEffect(() => {
  setTimeout(() => {}, timerMs);
}, [timerMs]);

useRef:引用类型的两种模式

useRef 在 TypeScript 中有两种使用模式:

1. DOM 元素引用

const divRef = useRef<HTMLDivElement>(null);

useEffect(() => {
  if (!divRef.current) return;
  // 现在可以安全访问 divRef.current
  doSomethingWith(divRef.current);
});

return <div ref={divRef}>...</div>;

2. 可变值引用

const intervalRef = useRef<number | null>(null);

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

区别:

  • DOM 引用:.current 是只读的,由 React 管理
  • 可变引用:.current 是可写的,由开发者管理

useImperativeHandle:暴露子组件方法

用于向父组件暴露子组件的特定方法:

// 子组件
export type CountdownHandle = {
  start: () => void;
};

const Countdown = forwardRef<CountdownHandle, Props>((props, ref) => {
  useImperativeHandle(ref, () => ({
    start() {
      alert("Start");
    },
  }));

  return <div>Countdown</div>;
});

// 父组件
function App() {
  const countdownEl = useRef<CountdownHandle>(null);
  
  useEffect(() => {
    countdownEl.current?.start();
  }, []);

  return <Countdown ref={countdownEl} />;
}

自定义 Hook 的类型处理

自定义 Hook 返回数组时,为避免 TypeScript 推断为联合类型,可以使用 const 断言:

function useLoading() {
  const [isLoading, setState] = useState(false);
  const load = (promise: Promise<any>) => {
    setState(true);
    return promise.finally(() => setState(false));
  };
  return [isLoading, load] as const; // 推断为 [boolean, typeof load]
}

替代方案:显式定义元组类型

return [isLoading, load] as [
  boolean, 
  (promise: Promise<any>) => Promise<any>
];

最佳实践建议

  1. 尽量使用最具体的类型(如 HTMLDivElement 而非 HTMLElement
  2. 避免滥用 any 类型,优先使用精确类型
  3. 对于可能为 null 的值,使用联合类型明确声明
  4. 自定义 Hook 返回多个值时,考虑使用对象而非数组
  5. 为自定义 Hook 编写完整的类型定义并导出

通过遵循这些 TypeScript 与 React Hooks 的结合使用原则,可以大大提高代码的类型安全性和可维护性。

react Cheatsheets for experienced React developers getting started with TypeScript react 项目地址: https://gitcode.com/gh_mirrors/rea/react

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郜毓彬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值