- Published on
Using React Context with TypeScript
In this article, we will explore how to use the React Context API with TypeScript by building a to-do app from scratch.
What is the React Context API?
React Context is one of the features of React that was introduced in React v.16.3. It allows you to share data on a global level between components without passing props explicitly, or it avoids prop drilling. It also helps us implement state management in our React apps without needing extra libraries such as Redux. When React Context API combined With TypeScript, it provides even stronger type checking and a better development experience. To get the most out of this tutorial, it is helpful to have a basic understanding of React and TypeScript.
To get started, let's create a new file called TodoContext.tsx
and define the shape of our todo item, which has an id
(number), text
(string), and completed
(boolean) property.
Next, let's define the shape of our context value. It has an array of todos
, as well as two functions: addTodo
, which adds a new todo to the list, and toggleTodo
, which toggles the completed status of a todo.
// Define the shape of our todo item
interface Todo {
id: number;
text: string;
completed: boolean;
}
// Define the shape of our context value
interface TodoContextValue {
todos: Todo[];
addTodo: (text: string) => void;
toggleTodo: (id: number) => void;
}
The next step is to import createContext
from React and create TodoContext
with an initial value that matches the TodoContextValue
interface.
import React, { createContext, useState } from 'react'
// Create the context with an initial value
const initialValue: TodoContextValue = {
todos: [],
addTodo: () => {},
toggleTodo: () => {},
}
export const TodoContext = createContext<TodoContextValue>(initialValue)
Once we have created the context, we will create the TodoProvider
component and define the state for our todos using the useState
hook. We also define the addTodo
to add a new todo to the state and toggleTodo
functions to update the state.
Next, we create the context value by combining the state and functions into an object.
Finally, we will render the TodoProvider
component, providing the context value to the TodoContext.Provider
component. We also render the children
prop, which represents the components that will consume the context. The final result is shown below.
import React, { createContext, useState } from 'react';
// Define the shape of our todo item
interface Todo {
id: number;
text: string;
completed: boolean;
}
// Define the shape of our context value
interface TodoContextValue {
todos: Todo[];
addTodo: (text: string) => void;
toggleTodo: (id: number) => void;
}
// Create the context with an initial value
export const TodoContext = createContext<TodoContextValue>({
todos: [],
addTodo: () => {},
toggleTodo: () => {},
});
// Create a provider component
export const TodoProvider: React.FC = ({ children }) => {
// Define the state for our todos
const [todos, setTodos] = useState<Todo[]>([]);
// Add a todo to the list
const addTodo = (text: string) => {
const newTodo: Todo = {
id: Date.now(),
text,
completed: false,
};
setTodos([...todos, newTodo]);
};
// Toggle the completed status of a todo
const toggleTodo = (id: number) => {
setTodos((prevTodos) =>
prevTodos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
};
// Create the context value
const contextValue: TodoContextValue = {
todos,
addTodo,
toggleTodo,
};
// Render the provider with the context value
return (
<TodoContext.Provider value={contextValue}>{children}</TodoContext.Provider>
);
};
Until now we have our context set up, The next step is to consume the data that our Context provides so let's create a new file called TodoList.tsx
:
import React, { useContext } from 'react'
import { TodoContext } from './TodoContext'
const TodoList: React.FC = () => {
// Consume the todo context
const { todos, toggleTodo } = useContext(TodoContext)
return (
<ul>
{todos.map((todo) => (
<li
key={todo.id}
style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
onClick={() => toggleTodo(todo.id)}
>
{todo.text}
</li>
))}
</ul>
)
}
export default TodoList
In the above code we have created TodoList
component then we import TodoContext
from our TodoContext.tsx
then we consume the todo context using the useContext
hook from React.
To complete our todo app, let's create a component that allows users to add new todos. Create a new file called AddTodoForm.tsx
:
import React, { useState, useContext } from 'react'
import { TodoContext } from './TodoContext'
const AddTodoForm: React.FC = () => {
// Consume the todo context
const { addTodo } = useContext(TodoContext)
const [text, setText] = useState('')
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
if (text.trim() !== '') {
addTodo(text)
setText('')
}
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Add a new todo"
/>
<button type="submit">Add</button>
</form>
)
}
export default AddTodoForm
In the above code we have created AddTodoForm
component and we define a state for the input text using the useState
hook as well as handleSubmit
function that adds a new todo to the list when the form is submitted.
Finally, to use our context in our app, we have to import TodoProvider
inside the App
component and wrap our components with the 'TodoProvider' to make the context available to them.
import React from 'react'
import { TodoProvider } from './TodoContext'
import TodoList from './TodoList'
import AddTodoForm from './AddTodoForm'
const App: React.FC = () => {
return (
<TodoProvider>
<h1>Todo App</h1>
<TodoList />
<AddTodoForm />
</TodoProvider>
)
}
export default App
Conclusion
In this article, we explored how to use React Context API with TypeScript. As you have seen, using React Context with TypeScript slightly different to plain JavaScript. With all the pieces in place, you can now run your todo app with React Context and TypeScript.
I hope this article has provided you with a clear understanding of how to create and use React Context with TypeScript, using a todo app as an example. Happy coding!