I am trying to use typescript with createContext. I am making a context wrapper class NavContext
and passing state variables as value to the provider.
Here is the code:
export const NavContext = createContext();
const NavBar:ReactFC<ReactNode | undefined>= ({ children }) => {
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
return (
<NavContext.Provider value={{ isDrawerOpen, setIsDrawerOpen }}>
<AppBar position="static">{children}</AppBar>
</NavContext.Provider>
);
};
But I get caret symbol ^^^^^^
beneath createContext()
and get typescript error as
TS2554: Expected 1 arguments, but got 0.
Please guide me on how to resolve this error as without typescript this code worked fine.
CodePudding user response:
This is the way I set up context with TS, by providing a default value:
Set up NavbarContext.tsx
:
type NavContextType = {
isDrawerOpen: boolean;
setIsDrawerOpen: React.Dispatch<React.SetStateAction<boolean>>;
}
export const NavContext = createContext<NavContextType | null>(null);
type ProviderType = {
children: React.ReactNode;
};
export default function NavBarStateProvider({ children }: ProviderType) {
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
return (
<NavContext.Provider value={{ isDrawerOpen, setIsDrawerOpen }}>
{children}
</NavContext.Provider>
);
}
Custom hook to use the context, useNavContext.tsx
:
export default function useNavContext() {
const context = useContext(NavContext);
if (!context) {
throw new Error("useNavContext must be used within a NavBarStateProvider");
}
return context;
}
CodePudding user response:
The problem is that contexts can be consumed without necessarily being a child of a Provider. Even if your app doesn't do this anywhere, you need to make TypeScript happy by passing the default context value to .createContext
. (Because the context value type looks to have a state setter in it, I'd assume that there will always be a provider - because you can't have a state setter outside the context of a React component.)
One option is to pass a meaningless argument to createContext
(just because it's required) and tell TypeScript that it's OK that the type doesn't match. Also note that createContext
should have a generic argument as well.
type NavContextType = {
isDrawerOpen: boolean;
setIsDrawerOpen: React.Dispatch<React.SetStateAction<boolean>>;
};
export const NavContext = createContext<NavContextType>(undefined!);
CodePudding user response:
You should define the types for provider and context clearly.
interface ProviderProps {
children: React.ReactNode
}
interface ContextValue {
isDrawerOpen: boolean;
setIsDrawerOpen: () => void;
}
export const NavContext = createContext<ContextValue>(
null as unknown as ContextValue
)
const NavBar = ({ children }: ProviderProps) => {
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
return (
<NavContext.Provider value={{ isDrawerOpen, setIsDrawerOpen }}>
<AppBar position="static">{children}</AppBar>
</NavContext.Provider>
);
};