前言
Redux 是一个流行的 JavaScript 应用状态管理库,它可以帮助我们管理复杂的应用状态并使其易于开发和维护。在本文中,我们将使用 Redux 来实现一个完整的 TodoList 应用,从而深入了解 Redux 的使用方法和优势。
什么是 TodoList 应用
TodoList 应用是一种常见的任务管理应用,它允许用户创建、编辑和删除任务。一个典型的 TodoList 应用通常具有以下功能:
- 添加任务:用户可以输入任务名称并添加到任务列表中。
- 编辑任务:用户可以编辑任务名称。
- 删除任务:用户可以删除任务。
- 完成任务:用户可以标记任务为已完成或未完成。
- 过滤任务:用户可以根据任务状态(已完成或未完成)来过滤任务列表。
Redux 的使用方法
Redux 的核心思想是将应用状态保存在单一的状态树中,并使用纯函数来更新状态。这个状态树称为 Store,而状态更新的函数称为 Reducer。Redux 还提供了一些辅助函数和中间件来帮助我们处理异步操作和副作用。
下面是 Redux 的基本使用方法:
1. 安装 Redux
我们可以使用 npm 或 yarn 来安装 Redux:
npm install redux
或
yarn add redux
2. 创建 Store
我们需要创建一个 Store 来保存应用状态。一个 Store 包含以下内容:
- State:应用状态。
- Reducer:状态更新函数。
- Action:描述状态更新的对象。
- Dispatch:触发状态更新的方法。
下面是创建一个简单的 Store 的示例代码:
// javascriptcn.com 代码示例 import { createStore } from 'redux' const initialState = { todos: [] } function reducer(state = initialState, action) { switch (action.type) { case 'ADD_TODO': return { ...state, todos: [...state.todos, action.payload] } case 'DELETE_TODO': return { ...state, todos: state.todos.filter(todo => todo.id !== action.payload) } case 'UPDATE_TODO': return { ...state, todos: state.todos.map(todo => { if (todo.id === action.payload.id) { return { ...todo, name: action.payload.name } } else { return todo } }) } case 'TOGGLE_TODO': return { ...state, todos: state.todos.map(todo => { if (todo.id === action.payload) { return { ...todo, completed: !todo.completed } } else { return todo } }) } default: return state } } const store = createStore(reducer)
3. 创建 Action
Action 是一个描述状态更新的对象,它包含一个 type 属性和一个可选的 payload 属性。type 属性用于描述状态更新的类型,而 payload 属性用于描述状态更新的数据。我们可以使用一个函数来创建 Action:
// javascriptcn.com 代码示例 function addTodoAction(todo) { return { type: 'ADD_TODO', payload: todo } } function deleteTodoAction(id) { return { type: 'DELETE_TODO', payload: id } } function updateTodoAction(todo) { return { type: 'UPDATE_TODO', payload: todo } } function toggleTodoAction(id) { return { type: 'TOGGLE_TODO', payload: id } }
4. 触发状态更新
我们可以使用 store.dispatch() 方法来触发状态更新。dispatch() 方法接受一个 Action 对象作为参数,并将其传递给 Reducer 函数进行状态更新。
store.dispatch(addTodoAction({ id: 1, name: 'Buy milk', completed: false })) store.dispatch(addTodoAction({ id: 2, name: 'Walk the dog', completed: false })) store.dispatch(toggleTodoAction(1)) store.dispatch(updateTodoAction({ id: 2, name: 'Walk the cat' })) store.dispatch(deleteTodoAction(1))
5. 获取状态
我们可以使用 store.getState() 方法来获取当前的应用状态。
const state = store.getState() console.log(state.todos)
实现一个完整的 TodoList 应用
现在我们已经了解了 Redux 的基本使用方法,接下来我们将使用 Redux 来实现一个完整的 TodoList 应用。我们将实现以下功能:
- 添加任务:用户可以输入任务名称并添加到任务列表中。
- 编辑任务:用户可以编辑任务名称。
- 删除任务:用户可以删除任务。
- 完成任务:用户可以标记任务为已完成或未完成。
- 过滤任务:用户可以根据任务状态(已完成或未完成)来过滤任务列表。
1. 创建 Store
我们需要创建一个 Store 来保存应用状态。我们的应用状态应该包含以下属性:
- todos:一个包含所有任务的数组。
- filter:当前的过滤器('all', 'completed' 或 'active')。
下面是创建 Store 的示例代码:
// javascriptcn.com 代码示例 import { createStore } from 'redux' const initialState = { todos: [], filter: 'all' } function reducer(state = initialState, action) { switch (action.type) { case 'ADD_TODO': return { ...state, todos: [...state.todos, action.payload] } case 'DELETE_TODO': return { ...state, todos: state.todos.filter(todo => todo.id !== action.payload) } case 'UPDATE_TODO': return { ...state, todos: state.todos.map(todo => { if (todo.id === action.payload.id) { return { ...todo, name: action.payload.name } } else { return todo } }) } case 'TOGGLE_TODO': return { ...state, todos: state.todos.map(todo => { if (todo.id === action.payload) { return { ...todo, completed: !todo.completed } } else { return todo } }) } case 'SET_FILTER': return { ...state, filter: action.payload } default: return state } } const store = createStore(reducer)
2. 创建 Action
我们需要创建以下 Action:
- addTodoAction(name):添加任务。
- deleteTodoAction(id):删除任务。
- updateTodoAction(todo):编辑任务。
- toggleTodoAction(id):标记任务为已完成或未完成。
- setFilterAction(filter):设置过滤器。
下面是创建 Action 的示例代码:
// javascriptcn.com 代码示例 let nextTodoId = 0 function addTodoAction(name) { return { type: 'ADD_TODO', payload: { id: ++nextTodoId, name, completed: false } } } function deleteTodoAction(id) { return { type: 'DELETE_TODO', payload: id } } function updateTodoAction(todo) { return { type: 'UPDATE_TODO', payload: todo } } function toggleTodoAction(id) { return { type: 'TOGGLE_TODO', payload: id } } function setFilterAction(filter) { return { type: 'SET_FILTER', payload: filter } }
3. 创建组件
我们需要创建以下组件:
- AddTodo:添加任务的输入框和按钮。
- TodoList:任务列表。
- TodoItem:单个任务。
- Footer:过滤器。
下面是创建组件的示例代码:
AddTodo
// javascriptcn.com 代码示例 import React, { useState } from 'react' import { useDispatch } from 'react-redux' import { addTodoAction } from '../redux/actions' function AddTodo() { const [name, setName] = useState('') const dispatch = useDispatch() function handleAddTodo() { if (name.trim() !== '') { dispatch(addTodoAction(name.trim())) setName('') } } function handleInputChange(e) { setName(e.target.value) } function handleKeyDown(e) { if (e.key === 'Enter') { handleAddTodo() } } return ( <div> <input type="text" placeholder="Add a todo" value={name} onChange={handleInputChange} onKeyDown={handleKeyDown} /> <button onClick={handleAddTodo}>Add</button> </div> ) } export default AddTodo
TodoList
// javascriptcn.com 代码示例 import React from 'react' import { useSelector } from 'react-redux' import TodoItem from './TodoItem' function TodoList() { const todos = useSelector(state => state.todos) return ( <ul> {todos.map(todo => ( <TodoItem key={todo.id} todo={todo} /> ))} </ul> ) } export default TodoList
TodoItem
// javascriptcn.com 代码示例 import React, { useState } from 'react' import { useDispatch } from 'react-redux' import { deleteTodoAction, updateTodoAction, toggleTodoAction } from '../redux/actions' function TodoItem(props) { const { todo } = props const [editing, setEditing] = useState(false) const [name, setName] = useState(todo.name) const dispatch = useDispatch() function handleDeleteTodo() { dispatch(deleteTodoAction(todo.id)) } function handleToggleTodo() { dispatch(toggleTodoAction(todo.id)) } function handleDoubleClick() { setEditing(true) } function handleBlur() { if (name.trim() === '') { setName(todo.name) } else { dispatch(updateTodoAction({ ...todo, name: name.trim() })) } setEditing(false) } function handleKeyDown(e) { if (e.key === 'Enter') { handleBlur() } else if (e.key === 'Escape') { setName(todo.name) setEditing(false) } } function handleInputChange(e) { setName(e.target.value) } return ( <li onDoubleClick={handleDoubleClick}> <input type="checkbox" checked={todo.completed} onChange={handleToggleTodo} /> {editing ? ( <input type="text" value={name} onChange={handleInputChange} onBlur={handleBlur} onKeyDown={handleKeyDown} /> ) : ( <span>{todo.name}</span> )} <button onClick={handleDeleteTodo}>Delete</button> </li> ) } export default TodoItem
Footer
// javascriptcn.com 代码示例 import React from 'react' import { useDispatch, useSelector } from 'react-redux' import { setFilterAction } from '../redux/actions' function Footer() { const filter = useSelector(state => state.filter) const dispatch = useDispatch() function handleSetFilter(filter) { dispatch(setFilterAction(filter)) } return ( <div> <button onClick={() => handleSetFilter('all')} disabled={filter === 'all'}> All </button> <button onClick={() => handleSetFilter('active')} disabled={filter === 'active'}> Active </button> <button onClick={() => handleSetFilter('completed')} disabled={filter === 'completed'}> Completed </button> </div> ) } export default Footer
4. 组合组件
我们需要将上面创建的组件组合起来以创建完整的 TodoList 应用。下面是组合组件的示例代码:
// javascriptcn.com 代码示例 import React from 'react' import AddTodo from './AddTodo' import TodoList from './TodoList' import Footer from './Footer' function App() { return ( <div> <AddTodo /> <TodoList /> <Footer /> </div> ) } export default App
总结
在本文中,我们使用 Redux 来实现了一个完整的 TodoList 应用,并深入了解了 Redux 的使用方法和优势。使用 Redux 可以帮助我们管理复杂的应用状态并使其易于开发和维护,因此它是一个非常有用的 JavaScript 应用状态管理库。我们希望本文能够对你学习 Redux 有所帮助,并能够启发你在实际项目中的应用。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/6566a6f2d2f5e1655dfa3c8e