[英]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.看起来您只需要一个接受S
的Partial
的单个动作,该动作被合并到 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>
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.