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



我在键入以下自定义Reacthook时遇到问题,我是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]
}

我应该在这里使用any吗?如果是,我如何正确使用any?既然这是通用函数,可以在任何地方使用,我该如何正确键入它?

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

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

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

接下来是减速器。看起来您只需要一个接受SPartial的操作,该操作将合并到状态中。因此,对于Reducer中的两个通用参数,我们使用S表示状态,使用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
)

或者,您可以键入reducer函数的参数,然后推断出这些类型,这看起来更干净,IMHO。

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

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

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

对于setStateCallback,我们需要接受一个Partial<S>来与完整状态合并,以及一个将完整状态作为唯一参数的回调:

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

你的效果应该很好。

最后一件事是将您的退货改为:

return [state, setStateCallback] as const

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

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

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

游乐场

相关内容

  • 没有找到相关文章

最新更新