Redux 是一种用于 JavaScript 应用程序的状态管理库,它可以与 React 等其他 JavaScript 库一起使用以构建高度可预测、高可维护的应用程序,同时减少了应用程序中各个组件之间的耦合。
在本文中,我们将通过实际的实例,详细讲解 Redux 在 React 应用程序中的工作原理,以及如何使用 Redux 进行状态管理。我们将首先介绍 Redux 的核心概念和工作原理,然后介绍如何在 React 中使用 Redux,最后通过示例代码演示如何用 Redux 管理应用程序的状态。
Redux 核心概念
Redux 作为一种状态管理库,其核心概念包括:Store、Action 和 Reducer。
Store
Store 是应用程序状态的单一可信源,其中保存着整个应用程序的状态。换言之,Store 作为一个 JavaScript 对象,它包含应用程序的所有状态和状态的更新方法。在 Redux 中,应用程序状态只能通过 dispatch 一个 action 来更新状态。
Action
Action 是一个 JavaScript 对象,它描述了应用程序中发生的每个事件的类型和相关的数据。当我们想要更改应用程序的状态时,需要 dispatch 一个 action,以告诉 Redux 应用程序中发生了什么事情。Actions 是一个纯粹的操作数据的对象,不包含任何逻辑或功能代码。
Reducer
Reducer 是一个纯函数,它接收两个参数,分别为当前应用程序的状态(state)和 dispatch 的 action 对象。Reducer 的作用是根据 action 类型来更新状态。具体地说,Reducer 接收一个旧的状态对象和一个 action 对象,并返回一个新的状态对象。在 Redux 中,Reducer 是唯一能够更改状态的地方,同时,也是应用程序中数据的最终来源。
在 React 中使用 Redux
在 React 中使用 Redux 的步骤包括:
- 安装依赖包
npm install redux react-redux --save
- 创建 Store 对象
在工程的根目录下创建一个 store.js 文件,然后在文件中定义一个 Store对象:
import { createStore } from 'redux';
import rootReducer from './reducers'; // import the reducers
// creating the Store
const store = createStore(rootReducer);
export default store;在上面的代码中,我们使用 createStore 方法来创建一个 Store 对象。rootReducer 是一个 Reducer 函数的集合,我们将其从 reducers.js 文件中导入。
- 编写 Reducer 函数
在 reducers.js 文件中,我们可以创建一些 reducer 函数,来处理来自不同 action 的状态更新:
// www.javascriptcn.com code example
import { combineReducers } from 'redux';
const initialState = {
todos: [],
};
function todoReducer(state = initialState, action) {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: state.todos.concat(action.payload),
};
case 'REMOVE_TODO':
return {
...state,
todos: state.todos.filter(todo => todo.id !== action.payload),
};
default:
return state;
}
}
export default combineReducers({
todoReducer,
});在上面的代码中,我们首先定义了一个包含 todos 数组的初始状态。然后,我们定义了 todoReducer 函数用于处理生成 type 为 ADD_TODO 和 REMOVE_TODO 的 action 类型。
在 ADD_TODO 操作中,我们使用 spread 运算符将当前状态对象的 todos 数组与新增的 todo 对象进行合并。REMOVE_TODO 的操作中,我们使用 filter 方法过滤掉指定 id 的 todo 对象来更新状态对象。最后,combineReducers 将所有的 reducer 函数进行整合,生成 root reducer,以供 createStore 函数使用。
- 构建容器组件
现在我们可以开始构建应用程序的容器组件了,容器组件会将应用程序的状态作为 props 传递给子组件,并用 dispatch 方法来将 action 对象传递给 Redux Store 对象。下面是一个示例 todo 应用程序的容器组件。
// www.javascriptcn.com code example
import React, { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { addTodo, removeTodo } from './actions';
function TodoList() {
const [inputValue, setInputValue] = useState('');
const todos = useSelector(state => state.todoReducer.todos);
const dispatch = useDispatch();
function handleInputChange(event) {
setInputValue(event.target.value);
}
function handleAddTodo() {
dispatch(addTodo({
text: inputValue,
id: Math.random().toString(16).slice(2),
}));
setInputValue('');
}
function handleRemove(id) {
dispatch(removeTodo(id));
}
return (
<div>
<h3>Todo List</h3>
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.text}
<button onClick={() => handleRemove(todo.id)}>Remove</button>
</li>
))}
</ul>
<div>
<input type="text" value={inputValue} onChange={handleInputChange} />
<button onClick={handleAddTodo}>Add Todo</button>
</div>
</div>
);
}
export default TodoList;在上面的代码中,我们调用 useSelector 函数来获取 todoReducer 的状态对象。同时,我们调用 useDispatch 函数来获取 Store 对象的 dispatch 方法,然后在添加和删除 todo 时使用该方法来更新应用程序的状态。
- 创建 Action
为了触发想要的状态的更新,我们需要定义一些 Action。下面是一个简单的 action:
// www.javascriptcn.com code example
export function addTodo(todo) {
return {
type: 'ADD_TODO',
payload: todo,
};
}
export function removeTodo(id) {
return {
type: 'REMOVE_TODO',
payload: id,
};
}在上面的代码中,我们定义了两个 Action 类型,ADD_TODO 和 REMOVE_TODO。在添加新todo时,我们使用 addTodo 函数来生成一个包含新的 todo 对象的 action 对象。同样,在删除todo 时,我们使用 removeTodo 函数来生成一个包含要删除的 todo 对象的 action 对象。
现在,我们已经准备好了 Redux 核心概念、在 React 应用程序中使用 Redux的步骤。
示例代码
最后,我们来看一个使用 Redux 的示例代码 - TodoList 应用程序。当用户添加或删除 todo 时,应用程序的状态将被更新。
安装依赖
打开终端,进入示例代码所在目录,然后执行如下命令:
npm install
运行应用程序
在项目目录下运行如下命令启动应用程序:
npm start
文件结构
├── App.js ├── actions.js ├── index.js ├── reducers.js ├── store.js ├── TodoList.js └── styles.css
App.js
// www.javascriptcn.com code example
import React from 'react';
import TodoList from './TodoList';
function App() {
return (
<div>
<TodoList />
</div>
);
}
export default App;actions.js
// www.javascriptcn.com code example
export function addTodo(todo) {
return {
type: 'ADD_TODO',
payload: todo,
};
}
export function removeTodo(id) {
return {
type: 'REMOVE_TODO',
payload: id,
};
}reducers.js
// www.javascriptcn.com code example
import { combineReducers } from 'redux';
const initialState = {
todos: [],
};
function todoReducer(state = initialState, action) {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: state.todos.concat(action.payload),
};
case 'REMOVE_TODO':
return {
...state,
todos: state.todos.filter(todo => todo.id !== action.payload),
};
default:
return state;
}
}
export default combineReducers({
todoReducer,
});store.js
import { createStore } from 'redux';
import rootReducer from './reducers';
const store = createStore(rootReducer);
export default store;TodoList.js
// www.javascriptcn.com code example
import React, { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { addTodo, removeTodo } from './actions';
function TodoList() {
const [inputValue, setInputValue] = useState('');
const todos = useSelector(state => state.todoReducer.todos);
const dispatch = useDispatch();
function handleInputChange(event) {
setInputValue(event.target.value);
}
function handleAddTodo() {
dispatch(addTodo({
text: inputValue,
id: Math.random().toString(16).slice(2),
}));
setInputValue('');
}
function handleRemove(id) {
dispatch(removeTodo(id));
}
return (
<div className='todo-list-container'>
<h3>Todo List</h3>
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.text}
<button onClick={() => handleRemove(todo.id)}>Remove</button>
</li>
))}
</ul>
<div>
<input type="text" value={inputValue} onChange={handleInputChange} />
<button onClick={handleAddTodo}>Add Todo</button>
</div>
</div>
);
}
export default TodoList;styles.css
// www.javascriptcn.com code example
.todo-list-container {
max-width: 800px;
margin: 0 auto;
}
input[type=text] {
width: 200px;
padding: 10px;
margin-right: 10px;
}
button {
padding: 10px;
background-color: #008CBA;
color: white;
border: none;
cursor: pointer;
}
button:hover {
background-color: #007B9F;
}我们已经学习了 Redux 的基本概念,以及如何在 React 中使用 Redux 进行状态管理。在实践过程中,你实际需要配置 Redux 的细节可能会更多,但基础的知识点已经被全部涵盖。通过认真学习和体验示例代码,相信能对 Redux 的更深层次内容有更进一步的理解。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d5b95ca941bf7134aebf5d