UseContext
Introduction
It is recommended to utilise useContext when a certain value needs to be re-used across the application, or when components don’t communicate directly with one another and passing props alone will be difficult or outright impossible.
Under in the Code section, I opted to share two examples. Option I, will throw an error and make an application unusable if not properly used - can be great to catch on errors, but not always the right fit. At times, due to an application’s complexity, Option II may be a better approach.
Breakdown
To properly use useContext, I prefer to divide it in three parts:
It is best to do these steps in its own file, to have all the logic in one place and thereby it should be easier to maintain.
Code
Option I
In this example, our context begins with a nullable version, where, if the context remains null, the application will throw out an error. Error handling can save up on a lot of development time, as this approach will complain if the hooks that belong to this provider are not used within said provider.
type UploadContextValue = {
isUpload: boolean;
setIsUpload: React.Dispatch<React.SetStateAction<boolean>>;
};
const UploadContext = React.createContext<UploadContextValue | null>(null);
export const UploadProvider = React.memo(
({ children }: { children: React.ReactNode; }) => {
const [isUpload, setIsUpload] = React.useState(false);
const value = React.useMemo(() => (
{
isUpload,
setIsUpload,
}
), [isUpload, setIsUpload]);
return (
<UploadContext.Provider value={value}>
{children}
</UploadContext.Provider>
);
});
const useNullableUploadContext = () => React.useContext(UploadContext);
const useUploadContext = () => {
const context = useNullableUploadContext();
if (context === null) {
throw new Error('No UploadContext defined.');
}
return context;
};
export const useUploadState = () => {
const context = useUploadContext();
return React.useMemo(() =>
[context.isUpload, context.setIsUpload] as const,
[context.isUpload, context.setIsUpload]);
};Option II
In contrast to the prior example, in this example, useContext starts with pre-defined values and will not throw an error even if it is being used incorrectly.
type DownloadContextValues = {
isOpen: boolean;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
};
const defaultValues = {
isOpen: false,
setIsOpen: () => {}
};
const DownloadContext = React.createContext<DownloadContextValues>(defaultValues);
export const DownloadProvider = React.memo(
({ children }: { children: React.ReactNode; }) => {
const [isOpen, setIsOpen] = React.useState<boolean>(false);
const value = React.useMemo(() => (
{
isOpen,
setIsOpen,
}
), [isOpen]);
return (
<DownloadContext.Provider value={value}>
{children}
</DownloadContext.Provider>
);
});
export const useIsDownloadOpen = () => {
const { isOpen, setIsOpen } = React.useContext(DownloadContext);
return React.useMemo(() => [isOpen, setIsOpen] as const, [isOpen, setIsOpen]);
};