Frontend Development 10 min read

Using React and Redux to Build a Todo List Application

This guide walks through creating a React Todo List app with Redux by installing the libraries, defining TypeScript state and actions for adding and deleting items, implementing an immutable reducer, configuring a store, wrapping the root with Provider, and connecting UI components to dispatch and display the todo array, illustrating the full Redux workflow and its debugging advantages.

Ximalaya Technology Team
Ximalaya Technology Team
Ximalaya Technology Team
Using React and Redux to Build a Todo List Application

Some students reported that state management in React Native is not very clear. This article introduces the typical workflow of using Redux for state management in both React and React Native, comparing its usage and pros/cons.

First, we demonstrate how to set up a React project and install Redux-related libraries.

npx create-react-app playpage_ts --template typescript
npm install @reduxjs/toolkit react-redux

Next, we define the data structures. In this example, a TODO is simply a text string, and the overall state contains an array of todos.

export type TODO = {\n    text: string\n}\n\nexport type State = {\n    todos: TODO[]\n}

We then define action types and action creators for adding and deleting todos.

// action types\nexport const ADD_TODO = 'ADD_TODO';\nexport const DELETE_TODO = 'DELETE_TODO';\n\n// action creators\nconst ACTION_CREATOR_ADD_TODO = (text: string) => ({ type: ADD_TODO, payload: text });\nconst ACTION_CREATOR_DELETE_TODO = (text: string) => ({ type: DELETE_TODO, payload: text });\n\nexport function DISPATCH_ADD_TODO(dispatch: any) {\n    return (text: string) => { dispatch(ACTION_CREATOR_ADD_TODO(text)); };\n}\n\nexport function DISPATCH_DELETE_TODO(dispatch: any) {\n    return (text: string) => { dispatch(ACTION_CREATOR_DELETE_TODO(text)); };\n}

The reducer processes these actions and returns a new state without mutating the previous one.

import { State, TODO } from "./model";\nimport { ADD_TODO, DELETE_TODO } from "./todoActions";\n\nconst initState: State = {\n    todos: [{ text: "clean room" }]\n};\n\nconst todoReducer = (state: State = initState, action: any): State => {\n    switch (action.type) {\n        case ADD_TODO:\n            return { todos: [...state.todos, { text: action.payload }] };\n        case DELETE_TODO:\n            const todos = state.todos.filter(item => item.text !== action.payload);\n            return { todos };\n        default:\n            return state;\n    }\n};\n\nexport default todoReducer;

We create a Redux store using the reducer.

import { createStore } from 'redux';\nimport todoReducer from './reducers';\n\nconst store = createStore(todoReducer);\nstore.subscribe(() => {\n    console.log('store changed >>>' + JSON.stringify(store.getState()));\n});\nexport default store;

To make the store available to the whole React app, we wrap the root component with Provider from react-redux .

import ReactDOM from 'react-dom/client';\nimport { Provider } from 'react-redux';\nimport ReduxTodoApp from './ReduxTodoApp';\nimport store from './redux/store';\n\nconst root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);\nroot.render(\n
\n
\n
\n);

The UI component ( ReduxTodoApp ) receives the todo list and dispatch functions via props.

import { useState } from "react";\nimport { connect } from "react-redux";\nimport { State, TODO } from "./redux/model";\nimport { DISPATCH_ADD_TODO, DISPATCH_DELETE_TODO } from "./redux/todoActions";\n\nfunction ReduxTodoApp({ todos, addTodo, deleteTodo }: { todos: TODO[]; addTodo: any; deleteTodo: any }) {\n    const [text, setText] = useState('');\n    const handleAddTodo = () => { addTodo(text); setText(''); };\n    const handleDeleteTodo = (t: string) => { deleteTodo(t); };\n    return (\n
\n
This Is Redux TODO App.
\n
\n                {todos.map((todo, index) => (\n
\n
{todo.text}
\n
handleDeleteTodo(todo.text)}>finish
\n
\n                ))}\n
\n
\n
setText(e.target.value)} />\n
Add Todo
\n
\n
\n    );\n}\n\nconst mapStateToProps = (state: State) => ({ todos: state.todos });\nconst mapDispatchToProps = (dispatch: any) => ({\n    addTodo: DISPATCH_ADD_TODO(dispatch),\n    deleteTodo: DISPATCH_DELETE_TODO(dispatch)\n});\n\nexport default connect(mapStateToProps, mapDispatchToProps)(ReduxTodoApp);

In summary, the classic Redux workflow consists of:

Define the data structure (State).

Define action types and creators (e.g., ADD_TODO , DELETE_TODO ).

Implement a reducer that returns a new state based on the action.

Create a global store with createStore .

Wrap the app with Provider to expose the store.

Use connect (or hooks) in UI components to map state to props and dispatch actions.

This separation of actions and reducers makes the codebase easier to extend and debug, providing the traceability that Redux promises.

TypeScriptReduxstate-managementReactFront-End DevelopmentToDo App
Ximalaya Technology Team
Written by

Ximalaya Technology Team

Official account of Ximalaya's technology team, sharing distilled technical experience and insights to grow together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.