如何键入自定义钩子 useStateWithCallback React TypeScript [英] How to type custom hook useStateWithCallback React TypeScript

查看:93
本文介绍了如何键入自定义钩子 useStateWithCallback React TypeScript的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在输入以下自定义 React hook 时遇到问题,我是 TypeScript 的新手,这引起了一些混乱.

I'm having problem to type the following custom React hook, I'm new to TypeScript and this is causing some confusion.

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]
}

我应该在这里使用 any,如果是这样,我该如何正确使用 any?既然这是通用功能,可以在任何地方使用,我该如何正确输入?

Should I use any here, if so how do I use any properly? Since this is universal function and can be used anywhere, how do I type it correctly?

我在我的示例中添加了一些我的尝试,并且,正如你所看到的,我停止了,因为从我的角度来看,它最终只会有 any 类型.

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.

推荐答案

首先,您需要让这个 useStateCallback 接受一个表示您的状态的通用参数.您将经常使用该参数.我们将其称为状态的 S.

First, you'll need to make this useStateCallback accept a generic parameter that represents your state. You're going to use that parameter a lot. We'll call that S for state.

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

接下来是减速器.看起来您只需要一个接受 Partial 的操作合并到状态中的 S.所以对于 Reducer 中的两个通用参数,我们使用 S 表示状态,Partial 表示动作.

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. So for the two generic parameters in Reducer we use S for the state and Partial<S> for the action.

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
)

或者你可以输入 reducer 函数的参数,这些类型将被推断出来,这看起来更简洁,恕我直言.

Or you could type the arguments of the reducer function, and those types would be inferred, which looks a bit cleaner, IMHO.

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

为了创建引用,我们需要给它一个回调函数的类型,与 null 联合,因为它可能并不总是包含一个值:

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:

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

对于 setStateCallback,我们需要接受一个 Partial 来与完整状态合并,以及一个具有完整状态的回调,因为它是唯一的参数:

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:

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

你的效果应该很好.

最后要做的就是将您的退货更改为:

Last thing to do would be to change your return to:

return [state, setStateCallback] as const

这是必需的,因为打字稿默认将其视为数组,但您希望它是元组.而不是 (S | Callback)[] 的数组,您希望它是一个正好包含两个 [S, Callback] 类型元素的元组.将 as const 附加到数组告诉打字稿将数组视为常量并将这些类型锁定到适当的位置.

This is required because typescript sees this as an array by default, but you want it to be a tuple. Instead of an array of (S | Callback)[] you want it be a tuple with exactly two elements of type [S, Callback]. Appending as const to the array tells typescript treat the array as a constant and lock those types into the proper positions.

把所有这些放在一起,你得到:

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>
}

游乐场

这篇关于如何键入自定义钩子 useStateWithCallback React TypeScript的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆