How to use React Context with TypeScript

How to use React Context with TypeScript

Improve developer experience by learning how to use TypeScript with React Context by building a to-do app from scratch.

Improve developer experience by learning how to use TypeScript with React Context by building a to-do app from scratch.

TypeScript has become increasingly popular due to numerous benefits recently added to the code, such as static type-checking robustness, understandability, and type interface.

And when TypeScript is used with React, it offers improved developer experience and more predictability to the project.

In this guide, we will learn how to use TypeScript with React Context by building a to-do app from scratch. Let’s dive in.

Setting up

To create a new app, I will use Create React App in order to have a modern configuration with no hassle. But, you are welcome to set up a new app from scratch using Webpack.

Begin by opening your terminal and running the following command:

npx create-react-app react-context-todo --template typescript

To enable TypeScript when using Create React App, you need to add the flag --template typescript, otherwise the app will support only JavaScript.

Next, let’s structure the project as follows:

├── src
|  ├── components
|  |  ├── AddTodo.tsx
|  |  └── Todo.tsx
|  ├── containers
|  |  └── Todos.tsx
|  ├── context
|  |  └── todoContext.tsx
|  ├── App.tsx
|  ├── index.css
|  ├── index.tsx
|  ├── react-app-env.d.ts
|  └── type.d.ts
├── tsconfig.json
├── package.json
└── yarn.lock

Here, there are two files to underline:

  • The context/todoContext.tsx file that serves as a context for the project
  • The type.d.ts file that contains the TypeScript Types. The extension .d.ts allows using the types in other files without importing them

With this in place, we can now get our hands dirty and code something meaningful.

Create the to-do type

TypeScript Type allows you to define what a variable or function should expect as a value in order to help the compiler catch errors before runtime.

type.d.ts

interface ITodo {
id: number
title: string
description: string
status: boolean
}type ContextType = {
todos: ITodo[]
saveTodo: (todo: ITodo) => void
updateTodo: (id: number) => void
}

As you can see, the interface ITodo defines the shape of a to-do object. Next, we have the type ContextType that expects an array of to-dos and the methods to add or update a to-do.

Create the context

React Context allows you to share and manage state across your components without passing down props. The context will provide the data to just the components that need to consume it.

context/todoContext.tsx

import * as React from "react";

export const TodoContext = React.createContext(null);

const TodoProvider: React.FC = ({ children }) => {
  const [todos, setTodos] = React.useState<ITodo[]>([
    {
      id: 1,
      title: "post 1",
      description: "this is my first description",
      status: false
    },
    {
      id: 2,
      title: "post 2",
      description: "this is my second description",
      status: true
    }
  ]);

Here, we start by creating a new context and set its type to match ContextType or null. The latter is using here to initialize the context with a null value. Next, we create the component TodoProvider that provides the context to the component consumers. Here, I initialize the state with some data to have todos to work.

context/todoContext.tsx

const saveTodo = (todo: ITodo) => {
  const newTodo: ITodo = {
    id: Math.random(), // not really unique - but fine for this example
    title: todo.title,
    description: todo.description,
    status: false,
  }
  setTodos([...todos, newTodo])
}

const updateTodo = (id: number) => {
  todos.filter((todo: ITodo) => {
    if (todo.id === id) {
      todo.status = true
      setTodos([...todos])
    }
  })
}

The function saveTodo will create a new to-do based on the interface ITodo and then append the object to the array of to-dos. The next function, updateTodo, will look for the id of the to-do passed as a parameter in the array of to-dos and then update it.

context/todoContext.tsx

 return (

      {children}

  );
};

export default TodoProvider;

Next, we pass the values to the context to make them consumable for the components.

context/todoContext.tsx

import * as React from 'react'

export const TodoContext = React.createContext(null)

const TodoProvider: React.FC = ({ children }) => {
  const [todos, setTodos] = React.useState<ITodo[]>([
    {
      id: 1,
      title: 'post 1',
      description: 'this is a description',
      status: false,
    },
    {
      id: 2,
      title: 'post 2',
      description: 'this is a description',
      status: true,
    },
  ])

  const saveTodo = (todo: ITodo) => {
    const newTodo: ITodo = {
      id: Math.random(), // not really unique - but fine for this example
      title: todo.title,
      description: todo.description,
      status: false,
    }
    setTodos([...todos, newTodo])
  }

  const updateTodo = (id: number) => {
    todos.filter((todo: ITodo) => {
      if (todo.id === id) {
        todo.status = true
        setTodos([...todos])
      }
    })
  }

  return (

      {children}

  )
}

export default TodoProvider

With this, we are now able to consume the context. So, let’s create the components in the next section.

Create the components and consume the context

components/AddTodo.tsx

import * as React from 'react'
import { TodoContext } from '../context/todoContext'

const AddTodo: React.FC = () => {
  const { saveTodo } = React.useContext(TodoContext) as ContextType
  const [formData, setFormData] = React.useState<ITodo | {}>()

  const handleForm = (e: React.FormEvent<HTMLInputElement>): void => {
    setFormData({
      ...formData,
      [e.currentTarget.id]: e.currentTarget.value,
    })
  }

  const handleSaveTodo = (e: React.FormEvent, formData: ITodo | any) => {
    e.preventDefault()
    saveTodo(formData)
  }

  return (
    <form className='Form' onSubmit={(e) => handleSaveTodo(e, formData)}>
      <div>
        <div>
          <label htmlFor='name'>Title</label>
          <input onChange={handleForm} type='text' id='title' />
        </div>
        <div>
          <label htmlFor='description'>Description</label>
          <input onChange={handleForm} type='text' id='description' />
        </div>
      </div>
      <button disabled={formData === undefined ? true : false}>Add Todo</button>
    </form>
  )
}

export default AddTodo

Here, we have a form component that allows handling data entered by the user using the useState hook. Once we get the form data, we use the function saveTodo pulled from the context object to add a new to-do.

Note that I use typecasting on the useContext hook to avoid TypeScript throwing errors because the context will be null at the beginning.

components/Todo.tsx

import * as React from 'react'

type Props = {
  todo: ITodo
  updateTodo: (id: number) => void
}

const Todo: React.FC<Props> = ({ todo, updateTodo }) => {
  const checkTodo: string = todo.status ? `line-through` : ''
  return (
    <div className='Card'>
      <div className='Card--text'>
        <h1 className={checkTodo}>{todo.title}</h1>
         {todo.description}
      </div>
      <button
        onClick={() => updateTodo(todo.id)}
        className={todo.status ? `hide-button` : 'Card--button'}
      >
        Complete
      </button>
    </div>
  )
}

export default Todo

As you can see here, we have a presentational component that shows a single to-do. It receives the to-do object and the function to update it as parameters that need to match the Props type defined above.

containers/Todos.tsx

import * as React from 'react'

import { TodoContext } from '../context/todoContext'
import Todo from '../components/Todo'

const Todos = () => {
  const { todos, updateTodo } = React.useContext(TodoContext) as ContextType
  return (
    <>
      {todos.map((todo: ITodo) => (

      ))}
    </>
  )
}

export default Todos

This component shows the list of to-dos when the page loads. It pulls the todos and the function updateTodo from the to-do context. Next, we loop through the array and pass to the Todo component the object to show. With this step forward, we are now able to provide the to-do context in the App.tsx file to finish up building the app. So, let’s use the context provider in the next part.

typescript react javascript web-development developer

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

How native is React Native? | React Native vs Native App Development

Article covers: How native is react native?, React Native vs (Ionic, Cordova), Similarities and difference between React Native and Native App Development.

Make an App with React JS / JavaScript: React and TypeScript

Building a Web Application as a Front-End Developer using React JS and TypeScript! Learn how to use the “Thinking in React” method and apply it to your real web application. We will be using real live weather data to display in our application and make our components reusable and maintainable.

How to Migrate a React Component from JavaScript to TypeScript

Learn how to easily migrate a React component from JavaScript to TypeScript to improve your project. When migrating over to TypeScript, one basic thing to keep in mind is TypeScript files have a .ts extension instead of .js. Another thing to keep in mind that makes migrating over a breeze is that you can configure your TypeScript compiler to allow JavaScript files.

Hire Dedicated React Native Developer

Have you ever thought of having your own app that runs smoothly over multiple platforms? React Native is an open-source cross-platform mobile application framework which is a great option to create mobile apps for both Android and iOS. **[Hire...

Hire Dedicated React Native Developer in India | React Native Development

Hire dedicated React Native developers for your next project. As the top react native development company we offer you the best service as per your business needs.