In my experience, I've encountered three different options for handling StateContext.
Method 1
This first method involves dealing with a nullable StateContext value, which can be either null
or of type StateContextType. While this approach requires extra checks for null values when using useContext
, the value will only be null during initialization and not inside the provider itself.
import React, { createContext, useState } from 'react';
type StateContextType = {
activeMenu: boolean;
setActiveMenu: React.Dispatch<React.SetStateAction<boolean>>;
};
export const StateContext = createContext<null | StateContextType>(null);
type ContextProviderProps = {
children: React.ReactNode;
};
export const ContextProvider = ({ children }: ContextProviderProps) => {
const [activeMenu, setActiveMenu] = useState(true);
const value = {
activeMenu,
setActiveMenu,
};
return (
<StateContext.Provider value={value}>{children}</StateContext.Provider>
);
};
Method 2
The second option involves explicitly casting the StateContext value to StateContextType. This means that the assumption is made that the value of StateContext will always be of type StateContextType, even though it is initially set to null
before being provided in StateContext.Provider.
Despite the initial null value, this approach is considered safe since a value is immediately provided in the provider.
import React, { createContext, useState } from 'react';
type StateContextType = {
activeMenu: boolean;
setActiveMenu: React.Dispatch<React.SetStateAction<boolean>>;
};
export const StateContext = createContext<StateContextType>(
null as unknown as StateContextType,
);
type ContextProviderProps = {
children: React.ReactNode;
};
export const ContextProvider = ({ children }: ContextProviderProps) => {
const [activeMenu, setActiveMenu] = useState(true);
const value = {
activeMenu,
setActiveMenu,
};
return (
<StateContext.Provider value={value}>{children}</StateContext.Provider>
);
};
Method 3
This third option was inspired by Steve Kinney's course on Frontend Masters on React and TypeScript.
import React, { useState } from 'react';
export function createContext<T>() {
const context = React.createContext<T | undefined>(undefined);
const useContext = () => {
const value = React.useContext(context);
if (value === undefined) {
throw new Error(
`useContext must be used inside a Provider with a value that's not undefined`,
);
}
return value;
};
return [useContext, context.Provider] as const;
}
type StateContextType = {
activeMenu: boolean;
setActiveMenu: React.Dispatch<React.SetStateAction<boolean>>;
};
export const [useContext, Provider] = createContext<StateContextType>();
type ContextProviderProps = {
children: React.ReactNode;
};
export const ContextProvider = ({ children }: ContextProviderProps) => {
const [activeMenu, setActiveMenu] = useState(true);
const value = {
activeMenu,
setActiveMenu,
};
return <Provider value={value}>{children}</Provider>;
};
const Component = () => {
// usage inside component
const context = useContext();
return <div></div>;
};
export const App = () => {
return (
<ContextProvider>
<Component />
</ContextProvider>
);
};