React context provides a way to pass data through the component tree without having to pass props down manually at every level. It’s essentially a global state management solution that allows you to share data between components without having to explicitly pass the data through props at every level of the component tree.
A Step-by-Step Guide to create a context with a custom provider in TypeScript
Create a Context
To create a React context use the React.createContext()
function. It creates a context object. The only argument to createContext
is the default value. If you don’t have a default value, specify null
.
In the following example, null is used as the initial value.
app-context.ts
import { createContext, useContext } from "react";
export interface IAppContextData {
props: boolean;
}
export interface IAppContext {
appData: IAppContextData;
setAppData: React.Dispatch<React.SetStateAction<IAppContextData>>;
}
export const initContextData: IAppContextData = {
props: false,
};
export const AppContext = createContext<IAppContext | null>(null);
Create a provider component
A provider component is a special component used in conjunction with React context to provide a context value to the component tree (children
). The <Context.Provider> component provides the context value to all the components within its subtree. The <Context.Provider> accepts the value
attribute (props) with data that you want to share with components that are descendants of this provider. Replace <Context.Provider>
with the name you assigned to your context. In this case, the context variable is named AppContext
, so you would replace <Context.Provider>
with <AppContext.Provider>
.
app-context-provider.tsx
import { ReactNode, useState } from "react";
import {
AppContext,
IAppContext,
IAppContextData,
initContextData,
} from "./app-context";
export default function AppContextProvider({
children,
}: {
children: ReactNode;
}) {
const [appData, setAppData] = useState<IAppContextData>(initContextData);
const contextValue: IAppContext = { appData, setAppData };
return (
<AppContext.Provider value={contextValue}>{children}</AppContext.Provider>
);
}
useState
within a custom context provider allows you to manage the state that you want to share across your application components. While context itself provides a way to pass down data through the component tree without having to pass props manually at every level, using useState
within a provider allows you to manage the state of that context data. Any components consuming AppContext
will have access to appData
and setAppData
, allowing them to read and update the state as needed. useState
lets you add context data to your application.
Provide a Context
Wrap the part of your component tree that needs access to the context with the custom provider component (<AppContextProvider>
).
Index.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import AppContextProvider from "./context/app-context-provider";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<React.StrictMode>
<AppContextProvider>
<App />
</AppContextProvider>
</React.StrictMode>
);
reportWebVitals();
The context won’t work within the component where it’s defined in the template. It will provide context data only to its children. The reason for placing the custom provider in the index.js file instead of the app.js file is precisely because AppContextProvider
is designed to provide context only to its children and it won’t have any effect if placed within the component itself.
Consume a Context
Components that want to access the context data can do it using the useContext
. useContext
is a React hook that allows components to consume context values.
Component.tsx
import { useContext } from "react";
import { AppContext, IAppContext } from "../context/app-context";
export default function Component() {
const { appData, setAppData } = useContext(AppContext) as IAppContext;
function changeContext() {
setAppData((prev) => ({
props: true,
}));
console.log(appData);
return (
<div>
<div>{JSON.stringify(appData)}</div>
<button onClick={changeContext}>Change context</button>
</div>
);
}
useContext(AppContext)
fetches the current context data. It returns the current context value for the specified context, which in this instance is AppContext
.