如何在createContext中为TypeScript初始化useState的set函数?

How to initialise the set function of useState for TypeScript in a createContext?

我有一个 Provider 通过两个 contexts.

提供状态变量及其对应的 setter
const BookedBatchContext = createContext({})
const SetBookedBatchContext = createContext(null)

const initialState = {
  id: null
}

Provider 看起来像这样:

export const BookedBatchProvider = ({ children }: { children: any }) => {
  const [bookedBatch, setBookedBatch] = useState(localState ||initialState)

  return (
    <SetBookedBatchContext.Provider value={setBookedBatch}>
      <BookedBatchContext.Provider value={bookedBatch}>
        { children }
      </BookedBatchContext.Provider>
    </SetBookedBatchContext.Provider>
  )
}

通过自定义挂钩,我使 setBookedBatch 可用于其他组件:

export const useBookedBatch = () => {
  const bookedBatch = useContext(BookedBatchContext)
  const setBookedBatch = useContext(SetBookedBatchContext)

  return { bookedBatch, setBookedBatch }
}

尝试使用 setBookedBatch 函数时,我在给定组件中收到以下错误:

setBookedBatch(selectedBatch)

错误:

TS2721: Cannot invoke an object which is possibly 'null'.

由于useState函数的setter不是我创建的函数,所以创建context的时候不知道怎么初始化:

const SetBookedBatchContext = createContext(null)

这样 TypeScript 就不会报错了。

  1. 如何知道setter函数的初始值?
  2. 如果我不提供任何类型,如何避免 TS 抱怨空值?

React.createContextReact.useState的return类型是根据您传入的初始值推断出来的。

1.) 您可以通过手动指定通用类型来创建适当的上下文类型:

const SetBookedBatchContext = createContext<null | React.Dispatch<React.SetStateAction<State>>>(null)

注意:useState 的 setter 类型为 React.Dispatch<React.SetStateAction<State>>,其中 StatelocalState || initialState 的类型。

2.) 在您的自定义 Hook useBookedBatch 中断言 setBookedBatch 不是 null:

export const useBookedBatch = () => {
  const bookedBatch = useContext(BookedBatchContext)
  const setBookedBatch = useContext(SetBookedBatchContext)
  if (setBookedBatch === null) throw new Error() // this will make setBookedBatch non-null
  return { bookedBatch, setBookedBatch }
  // returns: { bookedBatch: {}; setBookedBatch: React.Dispatch<React.SetStateAction<State>>; }
}

3.) 然后 setBookedBatch 之后可以在没有断言的情况下被调用:

const App = () => {
  const { setBookedBatch } = useBookedBatch()
  useEffect(() => { setBookedBatch({ id: "foo" }) }, [])
}

Sample on the playground