问题
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.
- How can I know the initial value of the setter function?
- 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