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

自闭症网瘾萝莉.ら 提交于 2021-01-29 09:46:24

问题


I have a Provider that provides a state variable and its corresponding setter through two contexts.

const BookedBatchContext = createContext({})
const SetBookedBatchContext = createContext(null)

const initialState = {
  id: null
}

The Provider looks like this:

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

Through a custom hook I make the setBookedBatch available to other components:

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

  return { bookedBatch, setBookedBatch }
}

When trying to use the setBookedBatch function, I get following error in a given component:

setBookedBatch(selectedBatch)

The error:

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

Since the setter of the useState function is a function that I didn't create, I don't know how to initialise it when I create the context:

const SetBookedBatchContext = createContext(null)

So that TypeScript does not complain.

  1. How can I know the initial value of the setter function?
  2. How can I avoid that TS complains about the null value, if I am not providing any types?

回答1:


The return types of React.createContext and React.useState are determined by inference from the initial values you pass in.

1.) You can create the proper context type by manually specifying the generic type:

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

Note: The setter for useState has type React.Dispatch<React.SetStateAction<State>>, where State is whatever localState || initialState is.

2.) Assert in your custom Hook useBookedBatch, that setBookedBatch is not 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.) Then setBookedBatch can be invoked without assertion later on:

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

Sample on the playground



来源:https://stackoverflow.com/questions/60712598/how-to-initialise-the-set-function-of-usestate-for-typescript-in-a-createcontext

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!