DEV Community

Savannah TechStack
Savannah TechStack

Posted on

React Context Api using TypeScript

Image description

Introduction

React Context API provides a way to pass data down the component tree without passing props. It's useful when passing down data that is Global for a component tree. For example, current authenticated user, theme, and preferred language in a multi-lingual app. It helps avoid props drilling problem.
We will build an app with several components where a global state of whether the user is logged in or not will be maintained. This state will be shared across components via context.

If you would prefer to follow this tutorial on YouTube it's available at the link below.

The final solution is available on GitHub

GitHub logo tndungu / React-Context-Api-Project

An application that uses Context Api to manage global state of logged in user

React Context Api Project

An app with 3 components, Login, Home and Settings, that demonstrates managing of Global state using React Context. LoggedIn flag for the user is provided from a Context Provider and other components subscribe to context changes to know whether a user is logged in or not. The app is created using React and TypeScript.

Local Setup

  1. Clone the Repository using the following command: git clone https://github.com/tndungu/React-Context-Api-Project.git
  2. Open the Repository using your favorite text editor. I use Visual Studio Code as a personal preference.
  3. Open terminal and run the following: npm install
  4. Run the project using npm start. This will open the project in http://localhost:3000

Video

There is a step by step guide on building the project on YouTube.

React TypeScript Student App




Prerequisites

This tutorial assumes you have some basic knowledge of using TypeScript with React. You may go through TypeScript with React Tutorial to get started.

App Development: Step by Step Guide

To start a new typescript app, use the following command

  • yarn:
yarn create-react-app context-typescript-app --template typescript
Enter fullscreen mode Exit fullscreen mode
  • npm:
npx create-react-app context-typescript-app --template typescript
Enter fullscreen mode Exit fullscreen mode

cd into student-app and yarn start OR npm start if using npm.

In the src folder, we will create a folder called components. Inside the folder lets create 3 simple components Login, Home and Settings. They will look as follows:

//Login.tsx
export const Login = () => {
    return (
        <>
            <div className="pageLayout">
                <div>
                    <h3>Login</h3>
                </div>
                <div>
                    <button>Login</button>
                </div>
            </div>
        </>
    );
};
Enter fullscreen mode Exit fullscreen mode
//Home.tsx
export const Home = () => {

    return (
        <div className='pageLayout'>
            <div>
                <h3>Home Page</h3>
            </div>

            <div>
            </div>
        </div>
    )
}

Enter fullscreen mode Exit fullscreen mode
//Settings
export const Settings = () => {
    return (
        <div className='pageLayout'>
            <div>
                <h3>Settings</h3>
            </div>

            <div>
            </div>
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

Import the components in the App.tsx file.

import './App.css';
import { Home } from './components/Home';
import { Login } from './components/Login';
import { Settings } from './components/Settings';

function App() {

  return (
    <>
      <Login />
      <Home />
      <Settings />
    </>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

Add the following Styles to the App.css file.

.App {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: center;
  flex-direction: column;
}

.pageLayout{
  display: flex;
  align-items: center;
  justify-content:space-between;
  border: 0.1rem solid tomato;
  border-radius: 0.3rem;
  width: 50%;
  height: 100px;
  margin: 10px;
}
button{
  width: 100px;
  height: 25px;
  background-color: aqua;
  border-radius: 5px;
  cursor: pointer;
}

div{
  margin: 10px;
  min-width: 100px;
}
.title{
  max-width: 100px;
}
Enter fullscreen mode Exit fullscreen mode

At this point, if you save all the files and run the app it should look like the below.
Image description

Create Context

In the App.tsx, we will create a context that will hold the state loggedIn which will be true if a user is logged in and false if a user is not logged in.

import './App.css';
import { Home } from './components/Home';
import { Login } from './components/Login';
import { Settings } from './components/Settings';
import { createContext, useState } from 'react'

export const LoginContext = createContext({ loggedIn: false, setLoggedIn: (loggedIn: false) => { } })

function App() {
  const [loggedIn, setLoggedIn] = useState<boolean>(false)

  return (
    <LoginContext.Provider value={{ loggedIn, setLoggedIn }}>
      <Login />
      <Home />
      <Settings />
    </LoginContext.Provider>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

In the code above, LoginContext will have an object with 2 properties loggedIn which is a boolean value and setLoggedIn which is a function hook that is used to set the loggedIn value.
The LoginContext object comes with Provider React Component that allows consuming components to subscribe to context changes. We will pass a value prop to LoginContext.Provider. This value will be propagated down the component tree to every component that subscribes to context changes.

useContext

We have created the context now it's time to consume it. In the components folder, let's add the simple component DisplayLogin.tsx which looks as follows. In addition, let's make the following changes to Login.tsx.

//DisplayLogin
export const DisplayLogin = () => {
  return (
    <div><h3>User is Logged in</h3></div>
  )
}

Enter fullscreen mode Exit fullscreen mode
//Login.tsx
import { useContext } from 'react'
import { LoginContext } from '../App'
import { DisplayLogin } from './DisplayLogin';

export const Login = () => {
    const { loggedIn, setLoggedIn } = useContext(LoginContext)

    return (
        <>
            <div className="pageLayout">
                <div>
                    <h3>Login</h3>
                </div>{!loggedIn &&
                    <DisplayLogin />
                }
                <div>
                    <button onClick={() => setLoggedIn(!loggedIn)}>Login</button>
                </div>
            </div>
        </>
    );
};

Enter fullscreen mode Exit fullscreen mode

From the Login.tsx component above, we have used the useContext hook to subscribe and consume the LoginContext. This enables us to get the global variable within Login.tsx without passing props. If you run the app, it should display as follows. Once you click the button, the message 'User is Logged in' is displayed.
Image description

Lets subscribe in the Home and Settings components as well. The 2 components will now look as follows:

//Home.tsx
import { useContext } from 'react'
import { LoginContext } from '../App'
import { DisplayLogin } from './DisplayLogin';

export const Home = () => {
    const { loggedIn, setLoggedIn } = useContext(LoginContext)
    return (
        <div className='pageLayout'>
            <div>
                <h3>Home Page</h3>
            </div>
            {!loggedIn &&
                    <DisplayLogin />
                }
            <div>
            </div>
        </div>
    )
}

Enter fullscreen mode Exit fullscreen mode
//Settings
import { useContext } from 'react'
import { LoginContext } from '../App'
import { DisplayLogin } from './DisplayLogin';

export const Settings = () => {
    const { loggedIn, setLoggedIn } = useContext(LoginContext)

    return (
        <div className='pageLayout'>
            <div>
                <h3>Settings</h3>
            </div>
            {!loggedIn &&
                <DisplayLogin />
            }
            <div>
            </div>
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

At this point, if you click the Login button, the message 'User is Logged in' is displayed on all components. This is because we have subscribed to the context of all the 3 components.

Image description

Refactoring Context

The useContext() has been used in all components. This is not best practice since it means we're exposing the whole context in every component whereas it might not be necessary to do so. In addition, there are duplications in our code. So we need to move our Context code to its file. We can also create a custom hook to wrap LoginContext.Provider. The final code will look as follows:

//App.tsx

import './App.css';
import { Home } from './components/Home';
import { Login } from './components/Login';
import { Settings } from './components/Settings';
import { LoginProvider } from './Context/LoginContext'

function App() {

  return (
    <LoginProvider>
      <Login />
      <Home />
      <Settings />
    </LoginProvider>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode
// Context/LoginContext
import React, { useState, createContext } from "react";

interface LoginProviderProps{
  children: React.ReactNode
}

export const LoginContext = createContext({loggedIn: false,setLoggedIn: (loggedIn: boolean) => {}});

export const LoginProvider = ({ children }: LoginProviderProps) => {

  const [loggedIn, setLoggedIn] = useState(false);

  return (
    <LoginContext.Provider value={{ loggedIn,setLoggedIn }}>
      {children}
    </LoginContext.Provider>
  );
};

Enter fullscreen mode Exit fullscreen mode
//useLoginContext

import {useContext} from 'react'
import { LoginContext } from '../Context/LoginContext'

export const useLoginContext = () => {
  return useContext(LoginContext)
}

Enter fullscreen mode Exit fullscreen mode
//Home.tsx
import { useLoginContext } from './useLoginContext'

export const Home = () => {
    const { loggedIn } = useLoginContext()

    return (
        <div className='pageLayout'>
            <div>
                <h3>Home Page</h3>
            </div>
            {loggedIn &&
                    <div><h3>User is Logged in</h3></div>
                }
            <div>
            </div>
        </div>
    )
}

Enter fullscreen mode Exit fullscreen mode
//Login.tsx
import { useLoginContext } from "./useLoginContext";

export const Login = () => {
    const { loggedIn, setLoggedIn } = useLoginContext()

    return (
        <>
            <div className="pageLayout">
                <div>
                    <h3>Login</h3>
                </div>
                {loggedIn &&
                    <div><h3>User is Logged in</h3></div>
                }
                <div>
                    <button onClick={() => setLoggedIn(!loggedIn)}>{loggedIn ? 'Logout' : 'Login'}</button>
                </div>
            </div>
        </>
    );
};

Enter fullscreen mode Exit fullscreen mode
//Settings
import { useLoginContext } from './useLoginContext'

export const Settings = () => {
    const {loggedIn } = useLoginContext()

    return (
        <div className='pageLayout'>
            <div>
                <h3>Settings</h3>
            </div>
            {loggedIn &&
                    <div><h3>User is Logged in</h3></div>
                }
            <div>
            </div>
        </div>
    )
}

Enter fullscreen mode Exit fullscreen mode

Conclusion

Congratulations! You have gone through all that is required to create and use Context in React using TypeScript. Context API is a popular way of managing the global state for small to medium-level applications. For large-scale applications, REDUX might be a better way of managing the state.
Feel free to comment below in case you need further assistance.

Top comments (0)