Redux
Redux 是一个用于管理和集中化应用状态的 JavaScript 库,常与 React 结合使用。通过 Redux,可以更有效地管理复杂应用中的状态,确保数据流的可预测性和可维护性。它通过使用被称为 “action” 的事件来触发状态更新,并以集中式 Store 的方式对整个应用的状态进行管理,确保状态只能以可预测的方式更新。
为什么使用 Redux?
在 React 应用中,组件通常通过 props 进行数据传递,但当应用变得复杂,组件层级加深时,状态管理可能变得困难。Redux 提供了一个集中式的状态管理方案,适用于以下情况:
-
多个组件需要共享状态:当多个组件需要访问和修改相同的状态时,Redux 提供了一个全局的状态存储,方便组件之间的状态共享。
-
状态频繁更新且逻辑复杂:对于需要频繁更新的状态,Redux 的单向数据流和严格的状态更新规则使得状态管理更加清晰和可预测。
-
中大型应用的协作开发:在多人协作开发中,Redux 的明确结构和规范有助于团队成员理解和维护代码。
Redux 的核心概念
-
Store(存储):应用的全局状态存储,保存所有的状态数据。
-
Action(动作):描述发生了什么的普通对象,包含一个 type 字段和其他必要的数据。
-
Reducer(归约器):纯函数,接收当前的 state 和 action,返回新的 state。
通过这三个核心概念,Redux 实现了状态的可预测管理。具体来说,Redux 通过 Reducer 来处理 action,并根据 action 的 type 来更新 state。
在 React 中使用 Redux
在 React 中使用 Redux 非常简单。只需要安装 Redux Toolkit 和 React Redux 这两个库,然后就可以在 React 组件中使用 Redux 的状态管理功能。
npm install @reduxjs/toolkit react-redux
作为工程化的最佳实践,一般将 Redux 相关的代码放在一个单独的文件夹中,比如 store 文件夹,然后根据应用的不同模块,创建多个 store 文件,最后在 store 文件夹中创建一个 index.js 文件,将所有模块的 store 进行组合,并导出 store
Redux 的基本使用
- 创建 Store
// createSlice 是 Redux Toolkit 提供的一个函数,用于简化 Redux 逻辑的编写。它自动生成 action creators 和 reducer,并将相关逻辑组织到一个“slice”中。 import { createSlice } from "@reduxjs/toolkit"; const counterSlice = createSlice({ // name 用来标识该 slice,还会被用作自动生成的 action 类型的前缀。例如,increment action 的类型会是 "counter/increment"。 name: "counter", // initialState 是一个对象,也就是随后我们在 reducers 中定义 action 处理函数所操作的 state。 // 状态对象将会存储在 Redux 的全局状态树中,并由这个 slice 进行管理。 // 这里的初始状态是一个包含 counter 属性的对象,初始值为 0。如果你的这个模块多个状态需要管理,可以在 initialState 中定义多个属性。 initialState: { counter: 0, // 用来记录 value 被改变的次数 value: 0, // 用来记录计数值 }, // reducers 中进行 reducer 的定义。 每个 reducer 都描述了当特定 action 被触发时,如何更新状态。 // Redux Toolkit 内部使用 Immer 库,使得在 reducer 中可以直接对 state 进行“可变”的操作,而实际上会产生一个新的不可变状态。 reducers: { // 当 increment action 被派发时,此 reducer 会被调用,将 state.counter 自增 1。虽然看起来是直接修改 state,但由于 Immer 的使用,实际上会返回一个新的状态。 increment: (state) => { state.value++; state.counter++; }, // 当 decrement action 被派发时,此 reducer 会将 state.counter 自减 1,同样是通过 Immer 来保证状态不可变性。 decrement: (state) => { state.value--; state.counter++; }, // Flux Standard Action(FSA)是一种约定,用来规范和标准化在 Flux 架构(以及由此衍生的 Redux)中所使用的 action 对象的结构。 // 规定 action 对象应包含一个 type 字段和一个可选的 payload 字段。 // type:一个字符串,用来描述这个 action 的类型,是区分不同 action 的唯一标识。 // payload:一个对象,用来传递额外的数据,通常用来表示 action 的细节。 // incrementByAmount reducer 接收一个 action 对象,其中包含一个 payload 属性,这个属性的值就是需要自增的数值。 incrementByAmount: (state, action) => { state.value += action.payload; state.counter++; }, // reset reducer 将整个 state 重置为初始状态。 reset: (state) => { state.counter = 0; state.value = 0; } }, }); // 通过解构赋值,从 counterSlice.actions 对象中提取出 increment、decrement、incrementByAmount 和 reset 四个 action creator,然后分别将它们导出。 export const { increment, decrement, incrementByAmount, reset } = counterSlice.actions; // 将 counterSlice.reducer 导出,作为 counterReducer。 export default counterReducer = counterSlice.reducer;
- 在根 Store(index.js)中组合所有的子 Store
import { configureStore } from "@reduxjs/toolkit"; import counterReducer from "./module/counterStore" // 使用 configureStore 方法来创建 Redux 的 store,并将一个名为 counterReducer 的 reducer 与名为 counter 的 slice 关联起来。 // 将所有的子模块的 reducer 聚合到一起,并返回一个 store 对象。 const store = configureStore({ reducer: { counter: counterReducer } }) export default store
- 为 React 组件提供 Store
import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; // Provider 是 react-redux 库提供的一个组件,其主要作用是将 Redux 的 store 提供给整个 React 组件树中的所有组件。 import { Provider } from 'react-redux'; import store from './store'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> {/* 通过将应用的根组件包裹在 Provider 内,并传入 store,使得组件树中的任何组件都可以访问到 Redux 的状态和派发动作,而无需手动传递 store。 */} <Provider store={store}> <App /> </Provider> </React.StrictMode> );
- 在 React 组件中使用 Redux 的状态和派发动作
import { useSelector, useDispatch } from "react-redux"; import { increment, decrement, reset, incrementByAmount } from "./store/module/counterStore"; function App() { // useSelector 允许组件从 Redux store 中读取数据。它的参数是一个回调函数,这个函数接收 Redux 的 state 作为参数,并返回组件需要的数据。组件只会在所选的 state 发生变化时重新渲染。 const { counter, value } = useSelector((state) => state.counter); // useDispatch 用于获取 dispatch 函数,调用 dispatch 以触发 Redux store 的状态更新。通过 dispatch 派发一个 action,Redux 就会执行相应的 reducer 逻辑,更新 store 中的状态。 const dispatch = useDispatch(); return ( <div className="App"> <button onClick={() => dispatch(decrement())}>-1</button> <span>计数值:{value} </span> <button onClick={() => dispatch(increment())}>+1</button> <button onClick={() => dispatch(incrementByAmount(3))}>+3</button> <button onClick={() => dispatch(reset())}>reset</button> <p>计数次数:{counter}</p> </div> ); } export default App;
经过以上四个步骤,我们已经实现了一个简单的 React 应用程序,它使用了 Redux 来管理状态,并通过 Redux Toolkit 来创建和组合 reducer。当你想更改状态的时候,只需要通过 dispatch 提交 action 即可触发 reducer,然后 Redux 会自动更新状态,并通知所有订阅该状态的组件进行重新渲染。
Redux 异步操作
Redux 有多种异步 middleware,每一种都允许你使用不同的语法编写逻辑。最常见的异步 middleware 是 redux-thunk,它可以让你编写可能直接包含异步逻辑的普通函数。Redux Toolkit 的 configureStore 功能默认自动设置 thunk middleware,我们推荐使用 thunk 作为 Redux 开发异步逻辑的标准方式。
以下是一个使用 Redux Toolkit 的 thunk middleware 来处理异步操作的示例:
import { createSlice } from "@reduxjs/toolkit";
const foodSlice = createSlice({
name: "foodSlice",
initialState: {
foods: [],
},
reducers: {
foodsList(state, action) {
state.foods = action.payload
}
}
})
// Redux Thunk 函数
const getFoodsList = () => {
return async (dispatch) => {
const res = await fetch('http://localhost:3004/takeaway')
const data = await res.json()
dispatch(foodSlice.actions.foodsList(data))
}
}
const {foodsList} = foodSlice.actions
export {foodsList, getFoodsList}
const foodReducer = foodSlice.reducer
export default foodReducer
const { foods } = useSelector(state => state.foodsList)
const dispatch = useDispatch()
useEffect(() => {
dispatch(getFoodsList())
}, [dispatch])
当我们定义了 getFoodsList 函数时,它返回了一个 thunk 函数,这个 thunk 函数可以异步地执行一些操作,然后调用 dispatch 来触发一个 action。调用 thunk 函数时总是将 (dispatch, getState) 作为它的参数,因此我们无需手动传递dispatch参数。