从懵圈到上手 Redux,我做出了属于自己的 TodoList

141 阅读6分钟

Redux 是一个流行的 JavaScript 状态管理库,特别适合管理大型应用中的复杂状态。本文将带你从零开始,一步步构建一个使用 Redux 管理的待办事项应用。

📌 一、什么是 Redux?为什么我们需要它?

想象一下,你正在开发一个 React 应用,里面有多个组件需要共享一些数据,比如用户信息、购物车内容、待办事项等等。

这时候你可能会遇到以下问题:

  • 组件之间如何共享数据?
  • 数据更新时,如何保证所有相关组件都能同步更新?
  • 如果数据更新逻辑变得复杂,该怎么管理?

Redux 就是为了解决这些问题而诞生的。

你可以把它想象成一个中央仓库(Store) ,所有组件都可以从这里获取数据,也可以通知它来修改数据。这样一来,数据就不再是某个组件的“私有财产”,而是整个应用的“公共资源”。

🌟 Redux 的三大核心概念:

  1. Store(仓库) :保存整个应用的状态。
  2. Action(动作) :描述发生了什么,比如“添加一个待办事项”。
  3. Reducer(状态管理函数) :根据动作来更新状态。

 二、项目初始化:创建 React 项目并安装 Redux 相关依赖

我们使用 create-react-app 快速搭建项目:

npx create-react-app redux-todo-demo
cd redux-todo-demo

接着安装 Redux 的两个关键依赖:

npm install @reduxjs/toolkit react-redux

📦 安装说明:

  • @reduxjs/toolkit:这是 Redux 官方推荐的工具包,它简化了 Redux 的使用方式,减少了很多样板代码。
  • react-redux:连接 React 和 Redux 的桥梁,让我们可以在 React 组件中方便地使用 Redux。

🧱 三、创建 Redux 子模块:待办事项模块

在 Redux 中,我们通常会将不同的功能模块化。比如,待办事项可以作为一个独立的模块,便于管理和维护。

✅ 创建文件:src/store/modules/todolistStore.js

import { createSlice } from '@reduxjs/toolkit'
// 使用 createSlice 创建一个切片(子模块)

const todolist = createSlice({
    name: 'todolist', // 模块名称,后续作为命名空间使用
    initialState: {
        list: [] // 初始状态是一个空数组
    },
    reducers: {
        // 添加待办事项
        addList: (state, action) => {
            state.list.push(action.payload)
        },
        // 删除指定索引的待办事项
        deleteList: (state, action) => {
            state.list.splice(action.payload, 1)
        }
    }
})

// 导出 actions
export const { addList, deleteList } = todolist.actions

// 导出 reducer
export default todolist.reducer

📖 代码解释:

  • createSlice:这是 Redux Toolkit 提供的 API,用于创建一个“切片”(slice),也就是一个独立的状态模块。

  • name:这个模块的名字,后续在状态树中会成为命名空间。

  • initialState:初始状态,我们这里是一个空数组,表示待办事项列表初始为空。

  • reducers:定义状态的更新方法,每个方法接收当前状态和一个 action。

    • addList:添加一个事项到列表中。
    • deleteList:根据索引删除一个事项。

💡 小贴士:Redux Toolkit 允许我们在 reducer 中“直接修改状态”,这是因为它内部使用了 Immer 库来处理不可变更新,不需要我们手动返回新对象。


🏗️ 四、创建总仓库:把模块整合起来

现在我们已经有了一个模块(todolist),接下来需要创建一个总仓库(store),把所有模块都整合进去。

✅ 创建文件:src/store/index.js

import { configureStore } from '@reduxjs/toolkit'
import todolistReducer from './modules/todolistStore'

// 创建并导出总仓库
export default configureStore({
    reducer: {
        todolist: todolistReducer
    }
})

📖 代码解释:

  • configureStore:这是 Redux Toolkit 提供的方法,用来创建一个 Redux 仓库。

  • reducer:在这里注册所有模块的 reducer。

    • todolist 是模块名,todolistReducer 是我们导出的 reducer。

💡 小贴士:虽然我们这里只有一个模块,但你可以继续添加更多模块,比如用户模块、计数器模块等,只需要在这里注册即可。


🔌 五、在 React 中使用 Redux:连接仓库

为了让整个 React 应用都能访问到 Redux 仓库,我们需要使用 react-redux 提供的 <Provider> 组件。

✅ 修改文件:src/index.js

import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import store from './store'
import App from './App'

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById('root')
)

📖 代码解释:

  • Provider:这是一个 React 组件,它把 Redux 的 store 传递给整个应用。
  • store:我们之前创建的总仓库,传给 Provider 后,所有子组件都可以通过 useSelector 和 useDispatch 来访问和修改状态。

🧩 六、在组件中使用 Redux:获取和修改状态

现在我们已经创建好了 Redux 仓库,并且定义好了状态(state)和操作方法(actions 和 reducers),接下来就可以在 React 组件中使用这些状态和方法了。

在 Redux 中,我们主要通过两个钩子函数来访问仓库:

  • useSelector:从仓库中获取状态(state)
  • useDispatch:从仓库中调用操作状态的方法(actions)

下面我们来详细看看如何使用它们。


✅ 示例 1:从仓库中获取状态 —— 显示待办事项列表

javascript
深色版本
import { useSelector } from 'react-redux'

function TodoList() {
    // 使用 useSelector 获取 Redux 仓库中的 list 状态
    const { list } = useSelector((state) => state.todolist)

    return (
        <ul>
            {list.map((item, index) => (
                <li key={index}>
                    {item}
                    <DeleteTodo index={index} />
                </li>
            ))}
        </ul>
    )
}

📌 说明:

  • useSelector 是一个 React 钩子函数,用于从 Redux 的仓库中获取数据。
  • 我们传入一个函数 (state) => state.todolist,它从整个状态树中提取出我们关心的模块(这里是 todolist)。
  • 然后我们从这个模块中取出 list,这就是我们保存的待办事项列表。
  • 每个事项都渲染成 <li> 标签,并在每个事项后面添加一个删除按钮组件 <DeleteTodo />

✅ 示例 2:调用仓库中的方法 —— 添加待办事项

javascript
深色版本
import { useDispatch } from 'react-redux'
import { addList } from '../store/modules/todolistStore'
import { useState } from 'react'

function AddTodo() {
    const dispatch = useDispatch()
    const [inputValue, setInputValue] = useState('')

    const handleAdd = () => {
        if (inputValue.trim()) {
            dispatch(addList(inputValue))
            setInputValue('')
        }
    }

    return (
        <div>
            <input
                value={inputValue}
                onChange={(e) => setInputValue(e.target.value)}
                placeholder="输入待办事项"
            />
            <button onClick={handleAdd}>添加</button>
        </div>
    )
}

📌 说明:

  • useDispatch 是另一个 React 钩子函数,它让我们可以“派发”(dispatch)一个 action。
  • addList 是我们之前定义的 action,用于添加一个新的待办事项。
  • 当用户点击“添加”按钮时,我们就调用 dispatch(addList(inputValue)),把输入框中的内容传入仓库。
  • 输入框的值我们使用了 React 的本地状态 useState 来管理。

✅ 示例 3:调用仓库中的方法 —— 删除待办事项

javascript
深色版本
import { useDispatch } from 'react-redux'
import { deleteList } from '../store/modules/todolistStore'

function DeleteTodo({ index }) {
    const dispatch = useDispatch()

    return (
        <button onClick={() => dispatch(deleteList(index))}>
            删除
        </button>
    )
}

📌 说明:

  • deleteList 是我们在模块中定义的另一个 action,用于删除指定索引的事项。
  • 我们通过 dispatch(deleteList(index)) 把索引值传给仓库。
  • 这个组件接收一个 index 属性,表示要删除的是第几个事项。

📚 七、TodoList(待办事项)应用

项目实现了以下功能:

✅ 添加新的待办事项
✅ 删除指定事项
✅ 全选/取消全选所有事项

image.png

项目地址:giteehttps://gitee.com/dragon-team-123/js_al/tree/master/react/react-redux1/src