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.
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-reduxNext, 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.
Ximalaya Technology Team
Official account of Ximalaya's technology team, sharing distilled technical experience and insights to grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.