React Context API allows us to share data between components without passing props manually through every level of the component tree. TypeScript can make Context API usage safer by defining the structure of the shared data and the functions used to update it.
In React Native applications, Context API can be useful for sharing information such as logged-in user data, theme settings, language, application settings, or authentication state.
Context API is a React feature that allows data to be shared between components without passing props manually from parent to child.
For example, a logged-in user can be made available to many screens through a single context.
Context API is useful when the same data is required by many components.
import { createContext } from "react";
const UserContext =
createContext(null);
However, using null alone does not describe the complete
structure of the context. TypeScript interfaces provide a better approach.
interface User {
id: number;
name: string;
email: string;
}
interface UserContextType {
user: User | null;
setUser: (user: User | null) => void;
}
The interface describes exactly what the context will contain.
const UserContext =
createContext<UserContextType | undefined>(
undefined
);
The context can initially be undefined and later contain
the complete typed context value.
const [user, setUser] =
useState<User | null>(null);
The user can either contain a User object or be null
when nobody is logged in.
function UserProvider(
{ children }: { children: React.ReactNode }
): React.JSX.Element {
return (
<UserContext.Provider
value={{ user, setUser }}
>
{children}
</UserContext.Provider>
);
}
The Provider makes the context value available to its child components.
function UserProvider(
{ children }: {
children: React.ReactNode
}
): React.JSX.Element {
// ...
}
React.ReactNode is commonly used to type content that can
be rendered as children.
import {
useContext
} from "react";
const context =
useContext(UserContext);
useContext() reads the current value supplied by the
nearest matching Provider.
const context =
useContext(UserContext);
if (!context) {
throw new Error(
"UserContext must be used inside UserProvider"
);
}
This check prevents the application from using the context outside its Provider.
function useUser(): UserContextType {
const context =
useContext(UserContext);
if (!context) {
throw new Error(
"useUser must be used inside UserProvider"
);
}
return context;
}
A custom hook makes context access easier throughout the application.
const { user } = useUser();
return (
<Text>
{user?.name}
</Text>
);
Optional chaining can be used because the user may be null.
const { setUser } = useUser();
const login = (): void => {
setUser({
id: 1,
name: "Rahul",
email: "rahul@example.com"
});
};
const logout = (): void => {
setUser(null);
};
Setting the user to null can represent a logged-out state.
interface AuthContextType {
user: User | null;
isLoggedIn: boolean;
setUser: (user: User | null) => void;
}
Context can contain more than one related value.
const [isLoggedIn, setIsLoggedIn] =
useState<boolean>(false);
Boolean state can be shared through the context.
setIsLoggedIn(true);
Context API works in React Native in the same general way as React.
It can be used to share application-level data between React Native components and screens.
import {
createContext,
useContext,
useState
} from "react";
<UserProvider>
<NavigationContainer>
<AppNavigator />
</NavigationContainer>
</UserProvider>
Placing the Provider above the navigator allows screens inside the navigator to access the context.
interface AppContextType {
user: User | null;
theme: "light" | "dark";
language: string;
}
A single context can contain several related application values.
interface AppContextType {
user: User | null;
login: (user: User) => void;
logout: () => void;
}
Function types allow components to safely call context actions.
In larger applications, the context can be placed in its own file.
src/
context/
UserContext.tsx
screens/
HomeScreen.tsx
ProfileScreen.tsx
components/
UserCard.tsx
This keeps the project organized and makes the context reusable.
interface User {
id: number;
name: string;
email: string;
}
const [user, setUser] =
useState<User | null>(null);
const loadUser =
async (): Promise<void> => {
const response =
await fetch(
"https://example.com/api/user"
);
const data: User =
await response.json();
setUser(data);
};
API data can be stored in context when multiple screens need access to it.
any for the entire context.null.any.User | null when the user may be logged out.useUser().Props are useful when data is passed directly from a parent to a child. Context is useful when the same data needs to be accessed by many components at different levels.
// Props
<Profile user={user} />
// Context
const { user } = useUser();
interface ThemeContextType {
theme: "light" | "dark";
setTheme:
(theme: "light" | "dark") => void;
}
Union types can restrict context values to known options.
setTheme("dark");
TypeScript can then detect invalid values such as:
setTheme("blue");
import React, {
createContext,
useContext,
useState
} from "react";
interface User {
id: number;
name: string;
email: string;
}
interface UserContextType {
user: User | null;
setUser: (user: User | null) => void;
}
const UserContext =
createContext<
UserContextType | undefined
>(undefined);
export function UserProvider(
{ children }: {
children: React.ReactNode
}
): React.JSX.Element {
const [user, setUser] =
useState<User | null>(null);
return (
<UserContext.Provider
value={{
user,
setUser
}}
>
{children}
</UserContext.Provider>
);
}
export function useUser():
UserContextType {
const context =
useContext(UserContext);
if (!context) {
throw new Error(
"useUser must be used inside UserProvider"
);
}
return context;
}
import React from "react";
import {
View,
Text,
Button
} from "react-native";
import {
useUser
} from "../context/UserContext";
export default function ProfileScreen():
React.JSX.Element {
const {
user,
setUser
} = useUser();
const login = (): void => {
setUser({
id: 1,
name: "Rahul",
email: "rahul@example.com"
});
};
const logout = (): void => {
setUser(null);
};
return (
<View>
<Text>
{user
? `Welcome ${user.name}`
: "Please Login"}
</Text>
<Button
title="Login"
onPress={login}
/>
<Button
title="Logout"
onPress={logout}
/>
</View>
);
}
Context API is useful for sharing application data between React Native components without passing props through every component.
With TypeScript, we can define interfaces for context values, users, functions, themes, authentication state, and other shared data.
You should now understand how to create a typed context, create a
Provider, use useContext(), create custom context hooks,
share user data, handle nullable state, and use Context API in a
React Native application.
createContext() to create a context.useContext() to read context data.useUser() can simplify context usage.User | null when the user may not be logged in.Question: Which React hook is used to read a Context value?