Reputation: 2588
I am setting up this custom hook with context provider based on this approach
const mainState = (initial: number | null = null) => {
const [edit, setEdit] = React.useState<number | null>(initial)
return {
edit,
clear: () => setEdit(null)
}
}
const MainContext = React.createContext<ReturnType<typeof mainState> | null>(null)
export const useMainContext = () => React.useContext(MainContext)
export function MainProvider({ children }: { children: React.ReactNode }) {
return (
<MainContext.Provider value={mainState()}>{children}</MainContext.Provider>
)
}
Then I am wrapping the whole app in the provider
import React from 'react'
import ReactDom from 'react-dom'
import { MainProvider } from './store'
import { App } from './app'
ReactDom.render(
<MainProvider>
<App />
</MainProvider>,
document.getElementById('root')
)
But when I try to get the hook TypeScript errors with
Property 'edit' does not exist on type '{ edit: number | null; clear: () => void; } | null'
import * as React from 'react'
import { useMainContext } from './store'
export const App = () => {
const { edit } = useMainContext()
...
}
Upvotes: 0
Views: 309
Reputation: 5112
As Typescript is telling you, the type of your context can either be null
or { edit: number | null; clear: () => void; }
Remember what you initially set it to: const MainContext = React.createContext<ReturnType<typeof mainState> | null>(null)
.
You can't destructure null so that's why Typescript isn't happy. Either you don't use destructuring:
const context = useMainContext()
context?.edit
or you set the context to a different value with a default value for edit.
I just noticed that mainState was using useState but mainState seems to be neither a custom react hook nor a component. The react docs states that a custom react hook should start with use
. And a hook has to be used in either another custom react hook OR a component.
So one way to fix your code:
const useMainState = (initial: number | null = null) => { // this is now a custom react hook
const [edit, setEdit] = React.useState<number | null>(initial)
return {
edit,
clear: () => setEdit(null)
}
}
export function MainProvider({ children }: { children: React.ReactNode }) {
// it's called in a component, Ok
const mainState = useMainState()
return (
<MainContext.Provider value={mainState}>{children}</MainContext.Provider>
)
}
Now React can keep track of your state. That's why it's also important never to use a hook after a if. The same number of hooks have to be used at all time in a component.
This quite similar to what you did, but you made the call in the prop and even if it worked, it's not that good design-wise.
Upvotes: 1