翼度科技»论坛 编程开发 JavaScript 查看内容

一文带你搞懂react hooks的类型声明

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18
在了解react hooks的类型之前,有必要先了解一下@types、.d.ts文件的概念及作用。

node_modules中的@types是什么?

当我们使用第三方npm包的时候,如果这个包不是
  1. ts
复制代码
编写,则没有导出类型,这时候如果在ts中导入会报错。比如
  1. jquery
复制代码
这时会报错
  1. 无法找到模块“jquery”的声明文件尝试使用 [code]npm i --save-dev @types/jquery
复制代码
(如果存在),或者添加一个包含
  1. declare module 'jquery';
复制代码
的新声明(.d.ts[/code]    这里提示找不到jquery的类型定义 可以安装
  1. @types/jquery
复制代码
或者在
  1. d.ts
复制代码
中自定义类型,大多数情况我们应该使用第一种办法,如果这个库没有
  1. @types
复制代码
库再使用第二种。

types查找规则

当我们使用
  1. import xx from
复制代码
时ts将会默认从
  1. ./node_modules/@types
复制代码
中获取类型声明,具体查找规则是ts编译器先在当前编译上下文找
  1. jquery
复制代码
的定义,找不到则再去
  1. ./node_modules/@types
复制代码
中查找。 在本地模块查找的类型声明作用域是在模块,在
  1. @types
复制代码
中的类型声明是全局的。在
  1. tsconfig.json
复制代码
中也可以使用
  1. typeRoots
复制代码
设置默认路径 。

模块types
  1. 当然在`tsconfig.json`中也可以使用`types`单独控制`@types`。`types`指定的包会被单独引入。这样全局引入就失效了。
复制代码
*.d.ts是什么
  1. @types下存放的文件都是.d.ts开头的文件 对应的npm包js的类型声明。 在.d.ts文件中声明的类型或者模块,在其他文件中不需要使用import导入,可以直接使用,d.ts的类型声明可以自行编写也可以使用工具声明。有2个工具
复制代码
可以使用微软的dts-gen,生成单个文件的声明dtsmake。值得注意的是如果你使用
  1. JSDOC
复制代码
语法 在ts3.7以后是可以通过命令为js生成.ds文件。具体用法可查看TypeScript: Documentation - Creating .d.ts Files from .js files (typescriptlang.org)
介绍完前菜 现在开始进入本文正题。 一起来看下react hooks相关的类型声明吧。在
  1. @types/react/index.d.ts
复制代码
文件中。

useContext
  1. `useContext和createContext`是结合一起使用的  
复制代码
useContext定义:
  1. function useContext<T>(context:Context<T>):T
复制代码
createContext定义:
  1. function createContext<T>(defaultValue:T,):Context<T>
复制代码
  1. createContext
复制代码
的返回Context类型的值提供给useContext的参数。这里泛型
  1. T
复制代码
在2个方法中是一致的,如果不指定 ts会类型推导出正确的类型。而
  1. Context
复制代码
类型 则是一个interface
  1. interface Context<T> {
  2.   Provider: Provider<T>;
  3.   Consumer: Consumer<T>;
  4.   displayName?: string | undefined;
  5. }
复制代码
  1. `Provider` 拥有`value`和`children` `Consumer`拥有 `children` 类型都是`ReactNode|undefined`。想想我们这react中使用`Context`传值 是不是感觉很熟悉?看懂类型定义 再也不怕忘记api了。
复制代码
useState

定义:
  1. function useState<S>(initialState:S| (() =>S)): [S, Dispatch<SetStateAction<S>>]
复制代码
泛型
  1. S
复制代码
表示
  1. state
复制代码
是用来约束
  1. initialState
复制代码
类型,也可以传入返回值是
  1. S
复制代码
的方法。
  1. useState
复制代码
返回值为2个元素的元组类型,返回
  1. state
复制代码
和更新
  1. state
复制代码
的方法。默认情况下
  1. useState
复制代码
会根据传入类型自动推导出
  1. S
复制代码
类型。
  1. SetStateAction<S>
复制代码
定义了传入
  1. setState
复制代码
的参数类型。是
  1. S
复制代码
类型或者返回
  1. S
复制代码
类型值的函数的联合类型。
  1. SetStateAction
复制代码
的定义为:
  1. type SetStateAction<S> = S|((prevState:S) =>S)
复制代码
  1. prevState
复制代码
为上一次的
  1. state
复制代码
,联合类型暂可以理解成的关系。而
  1. Dispatch
复制代码
表示setState的类型,是一个没有返回值的方法。定义也很简单
  1. Dispatch :type Dispatch<A> = (value:A) =>void
复制代码
。 还有
  1. useState
复制代码
参数个数为0的情况。上面的类型无法满足,所以后面个函数重载约束没有传入初始值的实现。
  1. function useState<S=undefined>(): [S|undefined, Dispatch<SetStateAction<S|undefined>>];
复制代码

useRef

定义比较简单:
  1. function useRef<T>(initialValue:T):MutableRefObject<T>
复制代码
,
  1. useRef
复制代码
返回一个可变 ref 对象,其
  1. .current
复制代码
属性初始化为传递的参数。
  1. MutableRefObject
复制代码
就是一个包含
  1. current:T
复制代码
的接口。值得注意的是 这里同样用了函数重载,包括了
  1. initialValue
复制代码
没有传或者为null的情况。
  1. ref
复制代码
  1. props
复制代码
中大部分的初始值都为
  1. null
复制代码
。 类型声明中注释明确指定了如果要使用可变的
  1. useRef
复制代码
则需要在范型参数中包含
  1. | null
复制代码
.
  1. * Usage note: if you need the result of useRef to be directly mutable, include `| null` in the type
  2.      * of the generic argument.
复制代码
如果我们这样写,此时ref为
  1. RefObject
复制代码
类型
  1. RefObject
复制代码
  1. current
复制代码
  1. readonly
复制代码
修饰。所以是不可变的。当在范型中指定了
  1. | null
复制代码
则根据函数重载命中第一种类型,返回
  1. MutableRefObject
复制代码
是可变的。
  1. const ref = useRef<number>(null)
  2. ref.current = 2 // 无法分配到 "current" ,因为它是只读属性。
  3. // 此时命中的这个重载的useRef
  4. function useRef<T>(initialValue: T|null): RefObject<T>;
复制代码
useEffect

定义:
  1. function useEffect(effect:EffectCallback, deps?:DependencyList):void
复制代码
  1. EffectCallback
复制代码
是一个只能返回
  1. void|Destructor
复制代码
的函数类型 用来处理副作用 。
  1. void
复制代码
表示没有返回值 ,但这里并不意味着你赋值一个有返回值的函数会报错,在一个返回值为
  1. void
复制代码
的函数你明确返回类型 并不会报错。而void真正表示无论你返回什么?编译器都不会使用检查它。
  1. Destructor
复制代码
表示析构函数,看下它的定义
  1. declare const UNDEFINED_VOID_ONLY: unique symbol;
  2. type Destructor = () => void | { [UNDEFINED_VOID_ONLY]: never }
复制代码
这里
  1. UNDEFINED_VOID_ONLY
复制代码
表示一个常量类型
  1. unique symbol
复制代码
  1. symbol
复制代码
的子类型 , 使用
  1. unique symbol
复制代码
的变量必须为
  1. const
复制代码
,而值为
  1. never
复制代码
表示的是那些永不存在的值的类型。
  1. never
复制代码
类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型。这里使用
  1. void
复制代码
和{
  1. [UNDEFINED_VOID_ONLY]: never }
复制代码
作为联合类型, 明确约束了
  1. effect
复制代码
是不能有返回类型的, 如果明确声明 则会报错。 如果有
  1. async
复制代码
修饰函数默认返回
  1. promise
复制代码
类型, 所以在
  1. useEffect
复制代码
中的
  1. effect
复制代码
也同样不能使用
  1. async
复制代码
  1. deps
复制代码
是可选参数,作为依赖是一个只读数组。
  1. ReadonlyArray
复制代码
是一个真正的只读数组类型,根据范型来约束数组元素类型。它没有改变数组的方法
  1. push
复制代码
  1. shift
复制代码
等。

useLayoutEffect
  1. useLayoutEffect
复制代码
类型声明与
  1. useEffect
复制代码
一致。但
  1. useLayoutEffect
复制代码
  1. callback
复制代码
会在
  1. DOM
复制代码
更新后同步触发 在浏览器同步刷新之前执行完成 可能会阻塞浏览器渲染。

useReducer

官方介绍
  1. useReducer
复制代码
  1. An alternative to
复制代码
useState
  1. .
复制代码
  1. useState
复制代码
的替代解决方案。一般我们都这样使用。当
  1. state
复制代码
结构或逻辑比较复杂时,用
  1. useReducer
复制代码
管理更方便容易。
  1. function reducer(state, action) {
  2.   switch (action.type) {
  3.     case 'increment':
  4.       return {count: state.count + 1};
  5.     case 'decrement':
  6.       return {count: state.count - 1};
  7.     default:
  8.       throw new Error();
  9.   }
  10. }
  11. const [state, dispatch] = useReducer(reducer, {count: 0});
  12. state.count
  13. dispatch({type: 'decrement'})
复制代码
在类型声明文件中
  1. useReducer
复制代码
写了5个重载函数类型。
  1. type ReducerWithoutAction<S> = (prevState: S) => S;
  2. type ReducerStateWithoutAction<R extends ReducerWithoutAction<any>> =
  3.         R extends ReducerWithoutAction<infer S> ? S : never;
  4. function useReducer<R extends ReducerWithoutAction<any>, I>(
  5.         reducer: R,
  6.         initializerArg: I,
  7.         initializer: (arg: I) => ReducerStateWithoutAction<R>
  8.     ): [ReducerStateWithoutAction<R>, DispatchWithoutAction];
复制代码

  • 第一种是
    1. reducer
    复制代码
    函数没有传
    1. action
    复制代码
    的情况。R表示
    1. reducer函数类型
    复制代码
    , 其中
    1. 参数state
    复制代码
    类型和返回类型必须一致。
    1. initializerArg
    复制代码
    表示初始参数,类型为泛型的第二个参数。
    1. initializer
    复制代码
    定义稍微复杂,但是其实约束了此类型必须是一个参数为
    1. initializerArg
    复制代码
    类型 返回值也同
    1. initializerArg
    复制代码
    类型一致的参数类型。而这个
    1. initializerArg
    复制代码
    就是
    1. reducer
    复制代码
    的参数
    1. state
    复制代码
    类型。
    1. ReducerStateWithoutAction
    复制代码
    就是为了约束这三个参数的类型。举个例子更清晰. 下述代码
    1. reducer
    复制代码
    1. state
    复制代码
    1. initializerArg
    复制代码
    已经
    1. initializer
    复制代码
    的参数和返回参数类型都应该保持一致。
  1. type stateType = {num: number}
  2. function reducer(state: stateType) {
  3.   return state
  4. }
  5. const [state,dispatch]=useReducer<typeof reducer,stateType>(
  6.   reducer, {num: 0},state=>{
  7.     return {num: state.num+1}
  8.   })
复制代码
这里的
  1. extends
复制代码
条件类型是一种条件表达式进行类型的关系检测,类似于三元表达式。意思为左侧类型可分配给右侧类型则返回?后面的类型 否则返回:后的类型。 而
  1. infer
复制代码
关键字只能出现在条件类型
  1. extends
复制代码
判断为
  1. true
复制代码
的分支,表示一个待推断的类型,
  1. infer S
复制代码
表示将推断的类型保存在
  1. S
复制代码
中。

  • 第二个重载与第一个类似 只是在
    1. initializer
    复制代码
    1. undefined
    复制代码
    的情况。如果在
    1. useReducer
    复制代码
    的泛型中指定了第二个参数,则命中第一个重载 此时会报错。具体实现类似下述代码。
  1. function useReducer<R extends ReducerWithoutAction<any>>(
  2.         reducer: R,
  3.         initializerArg: ReducerStateWithoutAction<R>,
  4.         initializer?: undefined
  5.     ): [ReducerStateWithoutAction<R>, DispatchWithoutAction];
  6. type stateType = {num: number}
  7. function reducer(state: stateType) {
  8.   return state
  9. }
  10. const [state,dispatch]=useReducer<typeof reducer>(
  11. reducer, {num: 0})
复制代码

  • 第三个重载约束了
    1. reducer
    复制代码
    函数传入
    1. action
    复制代码
    的情况,不同于
    1. redux
    复制代码
    1. action
    复制代码
    1. any
    复制代码
    类型。initializerArg初始参数为
    1. state
    复制代码
    与泛型
    1. I
    复制代码
    的交叉类型。
    1. I
    复制代码
    可能是
    1. state
    复制代码
    的子集的情况。
    1. ReducerState
    复制代码
    同样是为了取出
    1. reducer
    复制代码
    1. state
    复制代码
    类型。
    1. initializer
    复制代码
    同上述第一种重载类似。要约束
    1. arg
    复制代码
    1. initializerArg
    复制代码
    一致。而初始
    1. initializer
    复制代码
    的返回值要与
    1. reducer
    复制代码
    1. state
    复制代码
    一致。
  1. // Unlike redux, the actions _can_ be anything
  2. type Reducer<S, A> = (prevState: S, action: A) => S;
  3. // types used to try and prevent the compiler from reducing S
  4. // to a supertype common with the second argument to useReducer()
  5. type ReducerState<R extends Reducer<any, any>> = R extends Reducer<infer S, any> ? S : never;
  6. function useReducer<R extends Reducer<any, any>, I>(
  7.         reducer: R,
  8.         initializerArg: I & ReducerState<R>,
  9.         initializer: (arg: I & ReducerState<R>) => ReducerState<R>
  10.     ): [ReducerState<R>, Dispatch<ReducerAction<R>>];
复制代码
举个例子 初始参数
  1. initializer
复制代码
  1. state
复制代码
类型 在初始函数的参数类型也应该一致。
  1. // 代码实现
  2. type stateType = {num: number}
  3. type actionType = { type: string, payload: number}
  4. function reducer(state: stateType,action: actionType) {
  5.   if(action.type=='add'){
  6.     return {num: state.num+1}
  7.   }else {
  8.     return {num: state.num-1}
  9.   }
  10. }
  11. const [state,dispatch]=useReducer<typeof reducer,actionType>(
  12. reducer, { type: 'add', payload: 1,num: 2},state=>{
  13.   return {num:state.num+state.payload}
  14. })
复制代码

  • 第4个重载 和第三个类似 在初始参数不包括state的情况, 初始参数
    1. initializer
    复制代码
    1. state
    复制代码
    类型 在初始函数的参数类型也应该一致。
  1. function useReducer<R extends Reducer<any, any>, I>(
  2.         reducer: R,
  3.         initializerArg: I,
  4.         initializer: (arg: I) => ReducerState<R>
  5.     ): [ReducerState<R>, Dispatch<ReducerAction<R>>];
复制代码
第5个重载 和上述类似 约束了
  1. initializer
复制代码
  1. undefined
复制代码
,
  1. reducer
复制代码
存在
  1. actions
复制代码
的情况
  1. function useReducer<R extends Reducer<any, any>>(
  2.         reducer: R,
  3.         initialState: ReducerState<R>,
  4.         initializer?: undefined
  5.     ): [ReducerState<R>, Dispatch<ReducerAction<R>>];
复制代码
  1. useReducer
复制代码
的返回值都是一致。返回
  1. reducerState
复制代码
  1. Dispatch
复制代码
,而
  1. type Dispatch<A> = (value:A) =>void;
复制代码
就是一个没有返回值的函数 用来触发
  1. action
复制代码
改变
  1. reducerState
复制代码


useCallback

定义比较简单:
  1. function useCallback<T extends (...args:any[]) =>any>(callback:T, deps:DependencyList):T;
复制代码
范型
  1. T
复制代码
  1. function
复制代码
类型为第一个参数
  1. callback
复制代码
的类型,第二个参数
  1. DependencyList
复制代码
  1. useEffect
复制代码
的依赖数组一致,都是一个只读的数组。主要作用是用来缓存
  1. callback
复制代码
实例,当传递给子组件方法时与React.memo 或者shouldComponentUpdate一起使用。

useMemo

定义也比较简单:
  1. // allow undefined, but don't make it optional as that is very likely a mistake
  2. function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T;
复制代码
范型
  1. T
复制代码
  1. factory
复制代码
的返回值类型。
  1. deps
复制代码
依赖为
  1. DependencyList
复制代码
  1. undefined
复制代码
的联合类型,这里会有提示允许
  1. deps
复制代码
  1. undefined
复制代码
,但不能是可选的 否则可能是个错误。

useImperativeHandle
  1. useImperativeHandle
复制代码
主要用来配合
  1. forwardRef
复制代码
自定义暴露给父组件数据的。一般用来父组件调用子组件方法或获取子组件数据时使用。
  1. function useImperativeHandle<T, R extends T>(ref: Ref<T>|undefined, init: () => R, deps?: DependencyList): void;
  2. interface RefObject<T> {
  3.         readonly current: T | null;
  4.     }
  5.     // Bivariance hack for consistent unsoundness with RefObject
  6. type RefCallback<T> = { bivarianceHack(instance: T | null): void }["bivarianceHack"];
  7. type Ref<T> = RefCallback<T> | RefObject<T> | null;
复制代码
泛型
  1. T
复制代码
  1. ref
复制代码
  1. current
复制代码
的类型,
  1. R
复制代码
是第二个参数
  1. init
复制代码
方法的返回值,
  1. DependencyList
复制代码
同上述依赖数组一样 不可变数组。可以这样使用
  1. const Child = React.forwardRef<{num: number}>((prop,ref)=>{
  2.   useImperativeHandle<{num: number}, {num: number}>(ref,()=>({
  3.     'num': 1
  4.   }))
  5.   return (<div>123</div>)
  6. })
  7. const Foo = ()=>{
  8.   const childRef = useRef<{num: number}|null>(null)
  9.   useLayoutEffect(() => {
  10.     console.log(childRef.current?.num) // 1
  11.   }, [])
  12.   return <>
  13.     <Child ref={childRef}/>
  14.   </>
  15. }
复制代码
总结

本文根据阅读@types/react下
  1. hook
复制代码
相关源码入手,意在帮助大家熟悉常用hook以及类型声明 在开发时能得心应手 明白hooks的约束条件 更深入理解
  1. hook
复制代码
的功能。如上述内容有错误,请不吝指出
以上就是一文带你搞懂react hooks的类型声明的详细内容,更多关于react hooks的类型声明的资料请关注脚本之家其它相关文章!

来源:https://www.jb51.net/javascript/291963xuk.htm
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具