redux-toolkit 相关问题

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]);

一、RTK的简单使用

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 和 axios 版本的差异

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);
    }
  };
};
解释
  1. 按顺序执行:在 executeSequentially 函数中,await dispatch(getBillList()) 会等到 getBillList 执行完之后再执行下一个操作 addBillList,同样,await dispatch(addBillList(...)) 会等到添加账单操作完成后再执行 updateBillList

  2. 错误处理:每个异步请求都进行了 try/catch 错误处理,确保如果某个请求失败,后续的请求不会执行,并且错误信息能被捕获和记录。

  3. awaitawait 会阻止当前操作的继续执行,直到异步操作完成。这就是保证异步操作按顺序执行的关键。

三、异步处理时 createAsyncThunk 和 dispatch 的区别

1. 使用 createAsyncThunk 的方式

export const fetchCounter = createAsyncThunk(
  'counter/fetchCounter',
  async (payload) => {
    const response = await getDataApi(payload); // 调用你封装的请求
    return response.data; // 返回的数据会作为 action.payload
  }
);

优点:

  • 简洁与规范createAsyncThunk 是 Redux Toolkit 提供的内建功能,用于处理异步操作。它封装了异步逻辑,并自动管理 pendingfulfilledrejected 状态。你只需要关注异步操作的执行过程,Redux 会自动派发相关的状态更新。

  • 自动生成生命周期 actioncreateAsyncThunk 会自动生成与异步请求相关的 pendingfulfilledrejected 状态的 action 类型,因此你不需要手动处理这些状态。这对于处理加载、成功和失败状态特别有用。

  • 方便与 Reducer 结合createAsyncThunk 自动为异步操作添加了生命周期事件,可以直接与 extraReducers 配合使用来处理不同的状态。

缺点:

  • 灵活性较低:虽然 createAsyncThunk 提供了许多自动化的功能,但在复杂的异步逻辑(如多步异步请求、依赖关系)中,它可能不如手动 dispatch 灵活。

2. 直接使用 dispatchasync/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 等。这意味着你需要在多个地方编写代码来处理这些状态。

  • 代码冗长:对于简单的异步请求,使用 dispatchasync/await 的方式可能会导致冗长的代码。需要显式地编写 try/catch 和状态更新逻辑。

总结:

  • 推荐使用 createAsyncThunk

    • 如果你的异步操作较为简单,且你希望 Redux 管理整个异步请求的生命周期(包括 pending、fulfilled 和 rejected 状态),那么 createAsyncThunk 是更好的选择。

    • 适用于:简单的异步请求(如获取数据、提交表单等)。

  • 使用 dispatchasync/await

    • 如果你需要更多的控制,或者异步操作较为复杂(例如依赖多个异步操作、需要额外的逻辑等),则直接使用 dispatchasync/await 更加灵活。

    • 适用于:复杂的异步操作(如处理多个依赖的请求、多个步骤的异步操作等)。

在大多数情况下,createAsyncThunk 是推荐的方式,因为它提供了简洁的代码和良好的与 Redux Reducer 结合的能力。而手动使用 dispatchasync/await 则更适合于你需要更多控制的场景。

你可能感兴趣的:(react.js,javascript,前端)