example
store/module/billList.js
// 账单列表相关的store
import axios from "axios";
import { createSlice } from "@reduxjs/toolkit";
const billStore = createSlice({
name: "bill",
// 数据状态state
initialState: {
billList: [],
cartList: [],
},
reducers: {
// 同步修改方法
setBillList(state, action) {
state.billList = action.payload;
},
addBill(state, action) {
state.billList.push(action.payload);
},
addCart(state, action) {
// 是否添加过?以action.payload.id 去 cartList 中匹配 匹配到了 添加过
const item = state.cartList.find((item) => item.id === action.payload.id);
if (item) {
item.count++;
} else {
state.cartList.push(action.payload);
}
},
},
});
// 结构 actionCreater函数
const { setBillList, addBill, addCart } = billStore.actions;
// 编写异步
const getBillList = () => {
return async (dispatch) => {
const res = await axios.get("http://localhost:8888/list");
dispatch(setBillList(res.data));
};
};
const addBillList = (data) => {
return async (dispatch) => {
const res = await axios.post("http://localhost:8888/list", data);
dispatch(addBill(res.data));
};
};
export { getBillList, addBillList, addCart, setBillList, addBill };
// 导出reducer
const reducer = billStore.reducer;
export default reducer;
调用异步
import { useDispatch } from "react-redux";
import { getBillList } from "@/store/modules/billStore";
const dispatch = useDispatch();
useEffect(() => {
dispatch(getBillList());
}, [dispatch]);
1. 安装 toolkit 和 react-redux
npm install @reduxjs/toolkit react-redux
2. 利用 createSlice 构建store分片(store/modules/counter.js)
import { createSlice,createAsyncThunk } from "@reduxjs/toolkit";
// 1. 创建异步方法
export const fetchCounter = createAsyncThunk(
'counter/fetchCounter',
async (payload) => {
const response = await getDataApi(payload); // 调用你封装的请求
return response.data; // 返回的数据会作为 action.payload
}
);
// 2. 创建 slice
const counterSlice = createSlice({
name: "counter",
initialState: {
counter: 989,
},
reducers: {
addNumber(state, action) {
console.log("add number", action.payload);
state.counter = state.counter + action.payload;
},
subNumber(state, action) {
console.log("sub number", action.payload);
state.counter = state.counter - action.payload;
},
},
// 异步方法的处理逻辑(extraReducers)
// 不使用createAsyncThunk 的时候,不写extraReducers
extraReducers: (builder) => {
builder.addCase(fetchCounter.fulfilled, (state, action) => {
console.log("异步获取到的新 counter:", action.payload);
state.counter = action.payload;
});
}
});
//同步方法暴露出来
export const { addNumber, subNumber } = counterSlice.actions;
const counterReducer = counterSlice.reducer
export default counterReducer;
3. 利用configureStore 配置store(store/index.js)
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./modules/counter";
const store = configureStore({
reducer: {
counter: counterReducer,
},
});
export default store;
4. 利用react-redux,依照往常的做法,将store注入index.js
import React from "react";
import ReactDOM from "react-dom/client";
import { Provider } from "react-redux";
import App from "./App";
import store from "./store";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
//
//
);
5. 简单使用的例子
// Counter.js
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { addNumber, subNumber, fetchCounter } from "./counterSlice";
const Counter = () => {
const { count } = useSelector((state) => state.counter);
const dispatch = useDispatch();
return (
当前计数:{count}
);
};
export default Counter;
fetch版本的 getDataApi
// src/api/counter.js
export const getDataApi = async (payload) => {
try {
const res = await fetch(`/api/counter?id=${payload}`);
if (!res.ok) {
// fetch 请求状态码非 2xx 会走这里
throw new Error(`请求失败,状态码:${res.status}`);
}
const data = await res.json();
return { data }; // 返回的数据结构和 axios 保持一致
} catch (error) {
console.error("getDataApi 捕获错误:", error);
throw error; // 抛出错误让调用者处理(如 createAsyncThunk)
}
};
axios 版本的 getDataApi
// src/api/counter.js
import axios from 'axios';
export const getDataApi = async (payload) => {
try {
const res = await axios.get(`/api/counter`, {
params: { id: payload }
});
// 直接返回 res.data,因为 axios 自动解析 JSON
return { data: res.data }; // 保持和 fetch 版本结构一致
} catch (error) {
console.error("getDataApi 捕获错误:", error);
// 你也可以根据业务做更详细的错误处理,比如:
// if (error.response) {
// console.error("服务器返回错误:", error.response.data);
// }
throw error; // 抛出错误让调用者(如 createAsyncThunk)处理
}
};
fetch 和 axios 版本的差异说明:
对比点 | fetch | axios |
---|---|---|
错误处理 | 要手动判断 res.ok |
只要状态码非 2xx,自动抛出错误 |
数据提取 | await res.json() |
自动解析,直接用 res.data |
传参 | 手动拼接 URL 或写 URLSearchParams |
支持 params 参数自动序列化 |
假设你有三个操作:获取账单列表、添加一个账单和更新一个账单。你希望它们按顺序执行。你可以使用 async/await
来确保顺序:
const getBillList = () => {
return async (dispatch) => {
try {
const res = await axios.get("http://localhost:8888/list");
dispatch(setBillList(res.data));
} catch (error) {
console.error("获取账单列表失败:", error);
}
};
};
const addBillList = (data) => {
return async (dispatch) => {
try {
const res = await axios.post("http://localhost:8888/list", data);
dispatch(addBill(res.data));
} catch (error) {
console.error("添加账单失败:", error);
}
};
};
const updateBillList = (data) => {
return async (dispatch) => {
try {
const res = await axios.put("http://localhost:8888/list", data);
dispatch(updateBill(res.data));
} catch (error) {
console.error("更新账单失败:", error);
}
};
};
// 按顺序执行三个异步操作
const executeSequentially = () => {
return async (dispatch) => {
try {
// 执行 getBillList
await dispatch(getBillList());
// 执行 addBillList
await dispatch(addBillList({ amount: 100, description: "New bill" }));
// 执行 updateBillList
await dispatch(updateBillList({ id: 1, amount: 200 }));
} catch (error) {
console.error("异步操作执行失败:", error);
}
};
};
按顺序执行:在 executeSequentially
函数中,await dispatch(getBillList())
会等到 getBillList
执行完之后再执行下一个操作 addBillList
,同样,await dispatch(addBillList(...))
会等到添加账单操作完成后再执行 updateBillList
。
错误处理:每个异步请求都进行了 try/catch
错误处理,确保如果某个请求失败,后续的请求不会执行,并且错误信息能被捕获和记录。
await
:await
会阻止当前操作的继续执行,直到异步操作完成。这就是保证异步操作按顺序执行的关键。
1. 使用 createAsyncThunk
的方式
export const fetchCounter = createAsyncThunk(
'counter/fetchCounter',
async (payload) => {
const response = await getDataApi(payload); // 调用你封装的请求
return response.data; // 返回的数据会作为 action.payload
}
);
优点:
简洁与规范:createAsyncThunk
是 Redux Toolkit 提供的内建功能,用于处理异步操作。它封装了异步逻辑,并自动管理 pending
、fulfilled
和 rejected
状态。你只需要关注异步操作的执行过程,Redux 会自动派发相关的状态更新。
自动生成生命周期 action:createAsyncThunk
会自动生成与异步请求相关的 pending
、fulfilled
和 rejected
状态的 action 类型,因此你不需要手动处理这些状态。这对于处理加载、成功和失败状态特别有用。
方便与 Reducer 结合:createAsyncThunk
自动为异步操作添加了生命周期事件,可以直接与 extraReducers
配合使用来处理不同的状态。
缺点:
灵活性较低:虽然 createAsyncThunk
提供了许多自动化的功能,但在复杂的异步逻辑(如多步异步请求、依赖关系)中,它可能不如手动 dispatch
灵活。
dispatch
和 async/await
const updateBillList = (data) => {
return async (dispatch) => {
try {
const res = await axios.put("http://localhost:8888/list", data);
dispatch(updateBill(res.data));
} catch (error) {
console.error("更新账单失败:", error);
}
};
};
优点:
更高的灵活性:你可以自由控制异步操作的逻辑,包括如何处理错误、如何执行多个异步请求等。比如,你可以决定在 try/catch
中做额外的错误处理,或者在请求之间添加更多的逻辑。
无约束:你不需要遵循 createAsyncThunk
的生命周期逻辑,可以根据需求自由设计异步操作的流程。
缺点:
需要手动管理状态:在这种方式中,你需要手动管理异步操作的状态,例如 loading、error 等。这意味着你需要在多个地方编写代码来处理这些状态。
代码冗长:对于简单的异步请求,使用 dispatch
和 async/await
的方式可能会导致冗长的代码。需要显式地编写 try/catch
和状态更新逻辑。
总结:
推荐使用 createAsyncThunk
:
如果你的异步操作较为简单,且你希望 Redux 管理整个异步请求的生命周期(包括 pending、fulfilled 和 rejected 状态),那么 createAsyncThunk
是更好的选择。
适用于:简单的异步请求(如获取数据、提交表单等)。
使用 dispatch
和 async/await
:
如果你需要更多的控制,或者异步操作较为复杂(例如依赖多个异步操作、需要额外的逻辑等),则直接使用 dispatch
和 async/await
更加灵活。
适用于:复杂的异步操作(如处理多个依赖的请求、多个步骤的异步操作等)。
在大多数情况下,createAsyncThunk
是推荐的方式,因为它提供了简洁的代码和良好的与 Redux Reducer 结合的能力。而手动使用 dispatch
和 async/await
则更适合于你需要更多控制的场景。