简体   繁体   English

如何键入自定义钩子 useStateWithCallback React TypeScript

[英]How to type custom hook useStateWithCallback React TypeScript

I'm having problem to type the following custom React hook , I'm new to TypeScript and this is causing some confusion.我在输入以下自定义 React hook时遇到问题,我是 TypeScript 的新手,这引起了一些混乱。

const useStateCallback = (initialState: any) => {
  const [state, setState] = useReducer<Reducer<any, any>>((state, newState) => ({ ...state, ...newState }), initialState)
  const cbRef = useRef(null)

  const setStateCallback = (state, cb) => {
    cbRef.current = cb
    setState(state)
  }

  useEffect(() => {
    if (cbRef.current) {
      cbRef.current(state)
      cbRef.current = null
    }
  }, [state])

  return [state, setStateCallback]
}

Should I use any here, if so how do I use any properly?我应该在这里使用any ,如果是这样,我any如何正确使用? Since this is universal function and can be used anywhere, how do I type it correctly?由于这是通用的 function 并且可以在任何地方使用,我该如何正确输入?

I added some of my tryings right inside my example, and, as you can see I stop, because from my side of view it'll ends up with nothing but any types.我在我的示例中添加了一些我的尝试,并且,正如你所看到的,我停止了,因为从我的角度来看,它最终只会有any类型。

First, you'll need to make this useStateCallback accept a generic parameter that represents your state.首先,您需要让这个useStateCallback接受代表您的 state 的通用参数。 You're going to use that parameter a lot.您将经常使用该参数。 We'll call that S for state.我们称它为S表示 state。

function useStateCallback<S>(initialState: S) { ... }

Next up is the reducer.接下来是减速机。 It looks like you want just a single action that accepts a Partial of S that gets merged into the state.看起来您只需要一个接受SPartial的单个动作,该动作被合并到 state 中。 So for the two generic parameters in Reducer we use S for the state and Partial<S> for the action.因此对于Reducer中的两个通用参数,我们使用S表示 state 和Partial<S>表示动作。

const [state, setState] = useReducer<Reducer<S, Partial<S>>>(
  (state, newState) => ({ ...state, ...newState }),
  // state is implicitly typed as: S
  // newState is implicitly typed as: Partial<S>

  initialState
)

Or you could type the arguments of the reducer function, and those types would be inferred, which looks a bit cleaner, IMHO.或者,您可以键入减速器 function 的 arguments,然后会推断出这些类型,恕我直言,这看起来更简洁。

const [state, setState] = useReducer(
  (state: S, newState: Partial<S>) => ({ ...state, ...newState }),
  initialState
)

For creating the ref, we need to give it a type of the callback function, unioned with null since it may not always contain a value:为了创建 ref,我们需要给它一个类型的回调 function,与null联合,因为它可能并不总是包含一个值:

const cbRef = useRef<((state: S) => void) | null>(null)

for setStateCallback , we need to accept a Partial<S> to merge with the full state, and a callback that has the full state as it's only argument:对于setStateCallback ,我们需要接受Partial<S>以与完整的 state 合并,并接受具有完整 state 作为唯一参数的回调:

function setStateCallback(state: Partial<S>, cb: (state: S) => void) {
  cbRef.current = cb
  setState(state)
}

Your effect should be good as is.你的效果应该是好的。

Last thing to do would be to change your return to:最后要做的是将您的回报更改为:

return [state, setStateCallback] as const

This is required because typescript sees this as an array by default, but you want it to be a tuple.这是必需的,因为 typescript 默认将其视为一个数组,但您希望它是一个元组。 Instead of an array of (S | Callback)[] you want it be a tuple with exactly two elements of type [S, Callback] .而不是(S | Callback)[]的数组,您希望它是一个恰好包含两个[S, Callback]类型元素的元组。 Appending as const to the array tells typescript treat the array as a constant and lock those types into the proper positions.as const附加到数组告诉 typescript 将数组视为常量并将这些类型锁定到正确的位置。

Putting all that together, you get:把所有这些放在一起,你会得到:

import React, { useReducer, useRef, useEffect, Reducer } from 'react'

function useStateCallback<S>(initialState: S) {
  const [state, setState] = useReducer<Reducer<S, Partial<S>>>(
    (state, newState) => ({ ...state, ...newState }),
    initialState
  )
  const cbRef = useRef<((state: S) => void) | null>(null)

  function setStateCallback(state: Partial<S>, cb: (state: S) => void) {
    cbRef.current = cb
    setState(state)
  }

  useEffect(() => {
    if (cbRef.current) {
      cbRef.current(state)
      cbRef.current = null
    }
  }, [state])

  return [state, setStateCallback] as const
}

// Type safe usage
function Component() {
  const [state, setStateCallback] = useStateCallback({ foo: 'bar' })

  console.log(state.foo)

  setStateCallback({ foo: 'baz' }, newState => {
    console.log(newState.foo)
  })

  return <div>{state.foo}</div>
}

Playground 操场

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Typescript 带自定义反应钩 - Typescript with custom react hook React Typescript 自定义钩子排序列表 - React Typescript Custom Hook Sort List React &amp; Typescript 将 HTML 类型传递到自定义钩子中 - React & Typescript passing HTML types into custom hook React Custom Hook with Context and Typescript:属性“map”在类型“Option”上不存在 - React Custom Hook with Context and Typescript: Property 'map' does not exist on type 'Option' 如何使用 onClick 从自定义 React 挂钩 (API) 获取数据并使用 TypeScript 将其显示在 Div 中? - How to fetch data from a custom React hook (API) with onClick and display it in a Div using TypeScript? 如何修复自定义钩子中的 typescript 错误 - How fixed typescript error in custom hook 如何在 React 和 Typescript 中使用 useRef hook 和 getClientBoundingRect? - How to use useRef hook and getClientBoundingRect with React and Typescript? 如何使用 TypeScript 在 useRef 挂钩中键入函数 - How to type functions in useRef hook with TypeScript 如何创建一个自定义钩子,它的工作方式类似于 React with Typescript 中类组件中的 setState - How to create a custom hook, that works like setState in a class component in React with Typescript 打字稿未能推断自定义钩子中的解构数组类型 - typescript failed to infer destructuring array type in custom hook
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM