什么是 redux 和 redux tookit
- Redux:是一个独立的状态管理库,它不依赖于特定的前端框架(如 React),但常与 React 配合使用。其核心作用是集中管理应用中所有组件的状态,使状态变化可预测、可追踪。
- Redux Toolkit:是 Redux 官方推荐的工具库,它封装了 Redux 开发中的常见任务,简化了 Redux 的配置和使用。例如: 1.快速创建 store 2.简化 reducer 的编写 3.自动生成 action 和 action creator 4.内置处理异步操作的中间件
相关基础概念
1. 状态(State)
状态是一个存储应用数据的对象,它包含了应用运行过程中需要用到的各种信息。例如:
// 一个简单的状态示例
const state = {
user: { name: "张三", age: 25 },
theme: "light",
count: 0
};
这些数据会随着用户操作(如点击按钮、输入文本等)发生变化,状态的变化直接反映了应用的行为。
2. 状态管理
状态管理是一种设计模式,用于规范状态的创建、读取、更新和删除过程。它主要解决以下问题:
- 多个组件共享同一状态时,确保状态变化在所有组件中同步
- 避免组件嵌套过深时,通过 props 层层传递状态的繁琐操作
- 追踪状态的变化历史,便于调试和问题定位
3.Redux 设计理念
- 单一数据源:整个应用的状态被存储在一个统一的 store 中,便于集中管理和监控。
- 纯函数(Reducer):reducer 是修改状态的唯一入口,它接收当前状态和 action,返回新的状态,且过程中不会产生副作用(如修改参数、调用异步接口等)。
- Immutable(不可变性):Redux 规定状态一旦创建就不能被直接修改,每次状态更新都需要返回一个全新的状态对象。这一特性保证了状态变化的可追踪性。
注意:Redux Toolkit 通过内置的 Immer 库,允许我们在 reducer 中 “直接修改” 状态(如 state.value += 1),但这只是语法糖,底层仍会生成新的状态对象,不违反 immutable 原则。
- 通过 dispatch action 改变状态:store 中的状态只能通过 dispatch 一个 action 来修改,action 描述了 “要做什么”,reducer 根据 action 决定如何修改状态。 5.支持异步操作:通过 redux-thunk 中间件(Redux Toolkit 已默认集成),可以 dispatch 函数来处理异步操作(如接口请求)。
4. Redux 核心概念
- store:存储整个应用状态的容器,包含 getState()(获取状态)、dispatch(action)(触发状态更新)等方法。
- action:描述状态变化的普通对象,必须包含 type 属性(表示动作类型),可选包含 payload(传递的数据)。例如:{ type: ‘counter/increment’, payload: 1 }。
- reducer:根据 action 处理状态的纯函数,格式为 (state, action) => newState。
- dispatch:store 提供的方法,用于将 action 发送给 reducer,从而触发状态更新。
三、实战:使用 Redux Toolkit 实现计数器
下面通过一个计数器案例,演示 Redux Toolkit 的具体用法,包括环境搭建、store 配置、状态定义、组件使用等步骤。
1. 环境搭建
步骤 1:安装 create-react-app(用于创建 React 项目)
npm install react-create-app -g
步骤 2:创建 TypeScript 项目并进入目录
create-react-app redux-demo --template typescript
cd redux-demo
步骤 3:安装 Redux 与 Redux Toolkit
npm install redux redux-toolkit
步骤 4:启动项目
npm install
npm start
2. 创建 store
store 是存储状态的容器,通过 Redux Toolkit 的 configureStore 函数创建,代码如下:
// ./state/store.ts
import { configureStore } from "@reduxjs/toolkit";
export const store = configureStore({
reducer: {
// 后续会添加 reducer
},
});
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
configureStore:Redux Toolkit 提供的创建 store 的函数,内部已默认配置好常用中间件(如 redux-thunk)。 reducer:配置 store 对应的 reducer(状态更新函数),后续会将计数器的 reducer 添加到这里。 RootState:通过 ReturnType 提取 store 状态的类型,用于在组件中类型安全地获取状态。 AppDispatch:提取 dispatch 函数的类型,确保在组件中 dispatch 动作时类型正确。
3. 在应用中注入 store
通过 React-Redux 提供的 Provider 组件,将 store 注入整个应用,使所有组件都能访问 store:
///./index.ts
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import {Provider} from "react-redux";
import { store } from "./state/store";
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
<Provider store={store}>
:将 store 作为参数传递给 Provider 组件,Provider 会通过 React 的上下文(Context)机制,让所有子组件都能访问 store。
4. 创建计数器切片(Slice)
Redux Toolkit 中的 “切片(Slice)” 是一个包含 reducer 和 action 的对象,通过 createSlice 函数创建。计数器切片的代码如下:
//./state/counter/counterSlice.ts
import {createAsyncThunk, createSlice, PayloadAction} from "@reduxjs/toolkit";
interface CounterState {
value:number;
}
const initialState:CounterState = {
value:0,
}
const counterSlice = createSlice({
name:"counter",
initialState,
reducers:{
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action :PayloadAction<{value:number}>)=>{
state.value += action.payload.value;
}
},
extraReducers: (builder) => {
builder
.addCase(incrementAsync.pending,()=>{
console.log("incrementAsync.pending");
})
.addCase(incrementAsync.fulfilled, (state, action:PayloadAction<{value:number}>)=>{
state.value += action.payload.value;
})
},
});
export const incrementAsync = createAsyncThunk(
"counter/incrementAsync",
async (amount:number) => {
await new Promise((resolve) => setTimeout(resolve, 1000));
return {value:amount};
}
);
export const {increment, decrement ,incrementByAmount} = counterSlice.actions;
export default counterSlice.reducer;
关键代码解析:
- 定义状态类型与初始状态
interface CounterState {
value:number;
}
const initialState: CounterState = { value: 0 };
CounterState 接口定义了计数器状态的结构(仅有一个 value 属性,类型为数字)。 initialState 是计数器的初始状态,初始值为 0。
创建切片(createSlice)
- name: “counter”:切片名称,会作为 action 类型的前缀(如 counter/increment)。
- initialState:传入初始状态。
- reducers:定义同步 action 对应的处理函数(reducer):
- increment:每次调用时,将 state.value 加 1。
- decrement:每次调用时,将 state.value 减 1。
- incrementByAmount:接收一个包含 value 的 payload,将 state.value 加上该值。
- extraReducers:处理异步 action(如 incrementAsync)的状态变化,通过 builder 模式监听异步操作的不同阶段(pending、fulfilled 等)。
- 创建异步 action(createAsyncThunk)
export const incrementAsync = createAsyncThunk(
"counter/incrementAsync",
async (amount: number) => {
await new Promise((resolve) => setTimeout(resolve, 1000)); // 模拟接口请求延迟
return { value: amount };
}
);
- 用于创建处理异步操作的 action,第一个参数是 action 类型前缀,第二个参数是异步函数。
- 异步函数中通过 setTimeout 模拟接口请求(延迟 1 秒),成功后返回要传递的数据({ value: amount })。
- 异步操作有三个状态:pending(进行中)、fulfilled(成功)、rejected(失败),在 extraReducers 中分别处理。
reducers:{
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action :PayloadAction<{value:number}>)=>{
state.value += action.payload.value;
}
},
extraReducers: (builder) => {
builder
.addCase(incrementAsync.pending,()=>{
console.log("incrementAsync.pending");
})
.addCase(incrementAsync.fulfilled, (state, action:PayloadAction<{value:number}>)=>{
state.value += action.payload.value;
})
},
- 导出 action 与 reducer
- export const { increment, decrement, incrementByAmount } = counterSlice.actions:导出同步 action,用于在组件中触发状态更新。
- export default counterSlice.reducer:导出切片的 reducer,需添加到 store 中。
- 将切片 reducer 添加到 store 修改 store.ts,将计数器切片的 reducer 配置到 store 中:
// ./state/store.ts
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./counter/counterSlice"; // 导入计数器 reducer
export const store = configureStore({
reducer: {
counter: counterReducer, // 将计数器 reducer 添加到 store
},
});
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
此时 store 的状态结构为 { counter: { value: number } },counter 是切片对应的键名。
- 在组件中使用计数器状态 创建一个 Counter 组件,通过 useSelector 获取状态,通过 useDispatch 触发 action:
import { useDispatch, useSelector } from "react-redux";
import { AppDispatch, RootState } from "../state/store";
import { decrement, increment, incrementByAmount, incrementAsync } from "../state/counter/counterSlice";
const Counter = () => {
// 从 store 中获取计数器的值
const count = useSelector((state: RootState) => state.counter.value);
// 获取 dispatch 函数
const dispatch = useDispatch<AppDispatch>();
return (
<div>
<h2>{count}</h2>
<div>
{/* 触发同步 action */}
<button onClick={() => dispatch(increment())}>+1</button>
<button onClick={() => dispatch(decrement())}>-1</button>
<button onClick={() => dispatch(incrementByAmount({ value: 10 }))}>+10</button>
{/* 触发异步 action(延迟 1 秒后 +10) */}
<button onClick={() => dispatch(incrementAsync(10))}>Async +10</button>
</div>
</div>
)
}
export default Counter;
- useSelector:从 store 中提取需要的状态,参数是一个函数,接收整个状态对象,返回需要的部分(这里返回 state.counter.value)。
- useDispatch:获取 dispatch 函数,用于触发 action。通过 AppDispatch 类型约束,确保 dispatch 的 action 类型正确。
- 点击按钮时,通过 dispatch 调用对应的 action(同步或异步),从而更新计数器状态。
四、Redux 调试工具
Redux 提供了强大的调试工具 redux-devtools-extension,可以帮助我们追踪状态的变化历史,便于调试。
使用方法:
- 浏览器安装插件:在 Chrome 应用商店搜索 “Redux DevTools” 并安装。
- Redux Toolkit 的 configureStore 已默认集成调试工具,无需额外配置。
- 启动项目后,打开浏览器开发者工具(F12),切换到 “Redux” 标签页,即可查看:
- 每次 a`ction 的类型、 payload 及触发时间
- 状态的前后变化对比
- `可以重放、取消 action,观察状态变化