Lesson 77 of 60 – TypeScript with Context API
100%

TypeScript with Context API

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.

Note: When using Context API with TypeScript, the most important step is to define a proper interface for the context value.

1. What is Context API?

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.

2. Why Use Context API?

Context API is useful when the same data is required by many components.

  • Authentication information
  • Current user
  • Theme settings
  • Language settings
  • Application preferences
  • Shopping cart data

3. Creating a Context

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.

4. Creating a Context Interface

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.

5. Creating a Typed Context

const UserContext =
    createContext<UserContextType | undefined>(
        undefined
    );

The context can initially be undefined and later contain the complete typed context value.

6. Creating Context State

const [user, setUser] =
    useState<User | null>(null);

The user can either contain a User object or be null when nobody is logged in.

7. Creating the Provider

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.

8. Understanding children

function UserProvider(
    { children }: {
        children: React.ReactNode
    }
): React.JSX.Element {
    // ...
}

React.ReactNode is commonly used to type content that can be rendered as children.

9. Using useContext

import {
    useContext
} from "react";

const context =
    useContext(UserContext);

useContext() reads the current value supplied by the nearest matching Provider.

10. Checking an Undefined Context

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.

11. Creating a Custom Context Hook

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.

12. Reading User Data

const { user } = useUser();

return (
    <Text>
        {user?.name}
    </Text>
);

Optional chaining can be used because the user may be null.

13. Updating Context Data

const { setUser } = useUser();

const login = (): void => {

    setUser({
        id: 1,
        name: "Rahul",
        email: "rahul@example.com"
    });
};

14. Removing User Data

const logout = (): void => {

    setUser(null);

};

Setting the user to null can represent a logged-out state.

15. Adding Authentication State

interface AuthContextType {
    user: User | null;
    isLoggedIn: boolean;
    setUser: (user: User | null) => void;
}

Context can contain more than one related value.

16. Using Boolean State

const [isLoggedIn, setIsLoggedIn] =
    useState<boolean>(false);

Boolean state can be shared through the context.

setIsLoggedIn(true);

17. Context with React Native

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";

18. Providing Context to Navigation

<UserProvider>

    <NavigationContainer>

        <AppNavigator />

    </NavigationContainer>

</UserProvider>

Placing the Provider above the navigator allows screens inside the navigator to access the context.

19. Context with Multiple Values

interface AppContextType {
    user: User | null;
    theme: "light" | "dark";
    language: string;
}

A single context can contain several related application values.

20. Context with Functions

interface AppContextType {

    user: User | null;

    login: (user: User) => void;

    logout: () => void;
}

Function types allow components to safely call context actions.

21. Separating Context into a File

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.

22. Context with API Data

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.

23. Common Context TypeScript Mistakes

  • Using any for the entire context.
  • Not defining a context interface.
  • Forgetting that a user can be null.
  • Using the context outside its Provider.
  • Giving functions incorrect parameter types.
  • Using incorrect property names.
  • Putting unrelated application data into one large context.

24. Best Practices

  • Create an interface for the context value.
  • Use specific types instead of any.
  • Use User | null when the user may be logged out.
  • Create a custom hook such as useUser().
  • Keep Provider logic organized.
  • Place large contexts in separate files.
  • Keep unrelated state in separate contexts when appropriate.

25. Context and Props

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();

26. Context and Type Safety

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");

27. Complete User Context Example

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;
}

28. Using the Context in a Screen

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>
    );
}

29. Benefits of Context API with TypeScript

  • Reduces unnecessary prop drilling.
  • Provides centralized shared state.
  • Interfaces clearly describe shared data.
  • Function parameters can be type-safe.
  • Works well with React Native applications.
  • Custom hooks make context easier to consume.
  • TypeScript helps detect incorrect context usage during development.

30. Summary

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.

📌 Key Points

  • Context API allows data to be shared between components.
  • Create an interface for the context value.
  • Use createContext() to create a context.
  • Use a Provider to supply context values.
  • Use useContext() to read context data.
  • A custom hook such as useUser() can simplify context usage.
  • Use User | null when the user may not be logged in.
  • Type context functions such as login and logout.
  • Context API works well for shared React Native application state.

🧠 Quick Quiz

Question: Which React hook is used to read a Context value?