- React Context Api using TypeScript
- tndungu / React-Context-Api-Project
- An application that uses Context Api to manage global state of logged in user
- React Context Api Project
- Local Setup
- Video
- Prerequisites
- App Development: Step by Step Guide
- Create Context
- useContext
- Refactoring Context
- Conclusion
- TypeScript and React: Context
- Create a context #
- Provide context #
- Consume context #
- Context without default values #
React Context Api using TypeScript
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
- Clone the Repository using the following command: git clone https://github.com/tndungu/React-Context-Api-Project.git
- Open the Repository using your favorite text editor. I use Visual Studio Code as a personal preference.
- Open terminal and run the following: npm install
- 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.
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 create-react-app context-typescript-app --template typescript
npx create-react-app context-typescript-app --template typescript
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> /> ); >;
//Home.tsx export const Home = () => return ( div className='pageLayout'> div> h3>Home Page/h3> /div> div> /div> /div> ) >
//Settings export const Settings = () => return ( div className='pageLayout'> div> h3>Settings/h3> /div> div> /div> /div> ) >
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;
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; >
At this point, if you save all the files and run the app it should look like the below.
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] = useStateboolean>(false) return ( LoginContext.Provider value= <loggedIn, setLoggedIn >>> Login /> Home /> Settings /> /LoginContext.Provider> ); > export default App;
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> ) >
//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>& DisplayLogin /> > div> button onClick= => setLoggedIn(!loggedIn)>>Login/button> /div> /div> /> ); >;
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.
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> ) >
//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> ) >
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.
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;
// 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> ); >;
//useLoginContext import useContext> from 'react' import LoginContext > from '../Context/LoginContext' export const useLoginContext = () => return useContext(LoginContext) >
//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> ) >
//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> /> ); >;
//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> ) >
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.
TypeScript and React: Context
React’s context API allows you to share data on a global level. To use it, you need two things:
- A provider. Providers pass data to a subtree.
- Consumers. Consumers are components that consume the passed data inside render props
With React’s typings, context works without you doing anything else. Everything is done using type inference and generics. You consume all the implied type information which @types/react produces for you.
Create a context #
Let’s have a look! First, we create a context. The most important thing is to not forget default properties.
import React from 'react';
export const AppContext = React.createContext(
authenticated: true,
lang: 'en',
theme: 'dark'
>);
And with that, everything you need to do in terms of types is done for you. We have three properties called authenticated , lang and theme , they are of types boolean and string respectively. React’s typings take this information to provide you with the correct types when you use them.
Provide context #
Our app component provides this context. It also sets values different from default values.
const App = () =>
return AppContext.Provider value=
lang: 'de',
authenticated: true,
theme: 'light'
> >>
Header/>
/AppContext.Provider>
>
Now, every component inside this tree can consume this context. You already get type errors when you forget a property or use the wrong type:
const App = () =>
// ⚡️ compile error! Missing properties
return AppContext.Provider value=
lang: 'de',
> >>
Header/>
/AppContext.Provider>
>
Now, let’s consume our global state.
Consume context #
Consuming context is done via render props (see the previous chapter) for more details). You can destructure you render props as deep as you like, to get only the props you want to deal with:
const Header = () =>
return AppContext.Consumer>
(authenticated>) =>
if(authenticated)
return h1>Logged in!/h1>
>
return h1>You need to sign in/h1>
>
>
/AppContext.Consumer>
>
Because we defined our properties earlier with the right types, authenticated is of type boolean at this point. Again we didn’t have to do anything to get this extra type safety.
Context without default values #
The whole example above works best if we have default properties and values. Sometimes, you don’t have default values or you need to be more flexible in which properties you want to set.
Generics for createContext and the Partial helper can help you greatly with that:
import React from 'react';
// We define our type for the context properties right here
type ContextProps =
authenticated: boolean,
lang: string,
theme: string
>;
// we initialise them without default values, to make that happen, we
// apply the Partial helper type.
export const AppContext =
React.createContextPartialContextProps>>(>);
const Header = () =>
return AppContext.Consumer>
(authenticated>) =>
if(authenticated)
return h1>Logged in!/h1>
>
return h1>You need to sign in/h1>
>
>
/AppContext.Consumer>
>
// Now, we can set only the properties we really need
const App = () =>
return AppContext.Provider value=
authenticated: true,
> >>
Header/>
/AppContext.Provider>
>
There’s a StackBlitz you can check out.
And that’s context! I think it’s nicer to use with the hooks API, but nevertheless, it’s incredibly useful!