Redux Toolkit 调用 API 的四种方式

news2024/11/14 19:29:19

Redux Toolkit 调用 API 的四种方式

上篇笔记写的比较乱,加上这回又学了点新的东西,所以重新整合一下。

本地 API 用的是 json-server,端口设置在 3005,数据包含:

{
  "users": [
    {
      "id": 1,
      "name": "Myra"
    },
    {
      "name": "Pete Bernhard",
      "id": 9
    },
    {
      "name": "Bert Block",
      "id": 10
    },
    {
      "name": "Rachael Bayer",
      "id": 11
    }
  ]
}

基础写法

这种方法利用了 Redux Toolkit 现在会将同名的 reducers 与 actions 进行归并,对比使用 redux 不需要额外进行 actions 的声明,因此减少了代码量。

副作用的触发和逻辑放在 Component 中,优化方式可以通过写对应的 hooks 降低代码的重复率,或者使用 thunk。

  • slice

    与原生的 Redux 相比,RTK 新定义了一个 slice,将 reducers、actions、state 放入了 slice 中进行管理:

    import { createSlice } from '@reduxjs/toolkit';
    
    export const userSliceNative = createSlice({
      name: 'users',
      initialState: {
        data: [],
        isLoading: false,
        error: null,
      },
      reducers: {
        fetchingData(state) {
          state.isLoading = true;
        },
        fatchingDataCompleted(state) {
          state.isLoading = false;
        },
        setData(state, action) {
          state.data = action.payload;
        },
        setError(state, action) {
          state.data = action.error;
        },
      },
    });
    
  • store:

    store 的整合也较为简单,这里将 actions 放入了 store 中,这样其他组件可以直接从 store 中进行导入,非必需。

    import { configureStore } from '@reduxjs/toolkit';
    import logger from 'redux-logger';
    import { userSliceNative } from './slices/userSliceNative';
    
    export const store = configureStore({
      reducer: {
        userNative: userSliceNative.reducer,
      },
    });
    
    export const userNativeActions = userSliceNative.actions;
    
  • react component:

    Again,如果决定用这种写法了,useEffect 中的代码可以通过编写一个 custom hooks 进行一定程度上的优化,减少代码重复率。

    import axios from 'axios';
    import React, { useEffect } from 'react';
    import { useDispatch, useSelector } from 'react-redux';
    import { userNativeActions } from '../store';
    
    const UserNative = () => {
      const { data, isLoading, error } = useSelector((state) => state.userNative);
      const dispatch = useDispatch();
    
      useEffect(() => {
        dispatch(userNativeActions.fetchingData());
        axios
          .get('http://localhost:3005/users')
          .then((res) => {
            dispatch(userNativeActions.setData(res.data));
          })
          .catch((err) => dispatch(userNativeActions.setError(err)))
          .finally(dispatch(userNativeActions.fatchingDataCompleted()));
      }, []);
    
      let content;
    
      if (isLoading) {
        content = 'User is loading';
      } else if (error) {
        content = 'Load user fail';
      } else {
        content = data.map((user) => <div key={user.id}>{user.name}</div>);
      }
    
      return <div>{content}</div>;
    };
    
    export default UserNative;
    

thunk 实现

这里就是写了一个额外的 action creator,RTK 现在原生就支持 thunk 实现。

store 的配置和第一种写法一样,就不重复 cv 了。

  • slice:

    import axios from 'axios';
    // import from central store
    import { userNativeActions } from '..';
    
    import { createSlice } from '@reduxjs/toolkit';
    
    export const userSliceNative = createSlice({
      name: 'users',
      initialState: {
        data: [],
        isLoading: false,
        error: null,
      },
      reducers: {
        fetchingData(state) {
          state.isLoading = true;
        },
        fatchingDataCompleted(state) {
          state.isLoading = false;
        },
        setData(state, action) {
          state.data = action.payload;
        },
        setError(state, action) {
          state.data = action.error;
        },
      },
    });
    
    export const fetchUsersThunk = () => {
      return (dispatch) => {
        dispatch(userNativeActions.fetchingData());
    
        axios
          .get('http://localhost:3005/users')
          .then((res) => {
            dispatch(userNativeActions.setData(res.data));
          })
          .catch((err) => dispatch(userNativeActions.setError(err)))
          .finally(dispatch(userNativeActions.fatchingDataCompleted()));
      };
    };
    
  • component:

    import React, { useEffect } from 'react';
    import { useDispatch, useSelector } from 'react-redux';
    import { fetchUsersThunk } from '../store/thunks/fetchUsersThunk';
    
    const UserThunk = () => {
      const { data, isLoading, error } = useSelector((state) => state.userNative);
      const dispatch = useDispatch();
    
      useEffect(() => {
        dispatch(fetchUsersThunk());
      }, []);
    
      let content;
    
      if (isLoading) {
        content = 'User is loading';
      } else if (error) {
        content = 'Load user fail';
      } else {
        content = data.map((user) => <div key={user.id}>{user.name}</div>);
      }
    
      return <div>{content}</div>;
    };
    
    export default UserThunk;
    

asyncThunk

asyncThunk 是 RTK 内部提供的一个对异步 thunk 的处理方式,优点在于 Promise 的状态是 RTK 内部进行管理的,不需要用户手动实现。

  • slice:

    与完全靠用户手写一个 custom action creator,状态管理需要用户在 custom action creator 中判断并触发不太一样的一点就是,这里的 custom action creator 非常短,短到只需要返回调用成功时,reducer 中需要处理的结果。至于异步调用的 pending/fulfilled/rejected,这点基本由 RTK 进行管理。

    在 slice 中的 extraReducers 中,只需要根据 RTK 管理的状态更新对应的数据即可。

    import { createSlice } from '@reduxjs/toolkit';
    
    const userSlice = createSlice({
      name: 'users',
      initialState: {
        data: [],
        isLoading: false,
        error: null,
      },
      extraReducers(builder) {
        // retrieve
        builder.addCase(fetchUsers.pending, (state, action) => {
          state.isLoading = true;
        });
        builder.addCase(fetchUsers.fulfilled, (state, action) => {
          state.isLoading = false;
          state.data = action.payload;
        });
        builder.addCase(fetchUsers.rejected, (state, action) => {
          state.isLoading = false;
          state.error = action.error;
        });
      },
    });
    
    export const fetchUsers = createAsyncThunk('users/fetch', async () => {
      const response = await axios.get('http://localhost:3005/users');
    
      return response.data;
    });
    
    export const usersReducer = userSlice.reducer;
    
  • store:

    store 的挂载方式也与之前的一样

    import { configureStore } from '@reduxjs/toolkit';
    import { usersReducer } from './slices/usersSlice';
    import logger from 'redux-logger';
    import { userSliceNative } from './slices/userSliceNative';
    
    export const store = configureStore({
      reducer: {
        userNative: userSliceNative.reducer,
        users: usersReducer,
      },
      middleware: (getDefaultMiddleware) => {
        return getDefaultMiddleware().concat(logger);
      },
    });
    
  • component:

    关于 dispatch、useState 这块代码也可以另外封装一个 custom hooks 去进行实现。使用 asyncThunk 的封装应该相对而言更加容易,毕竟 Promise 的三个状态都是有 RTK 进行定义和管理的,基本上可以保证一致性。

    function UsersList() {
      const [isLoadingUsers, setIsLoadingUsers] = useState(false);
      const [loadingUsersError, setLoadingUsersError] = useState(null);
      const dispatch = useDispatch();
      const { data } = useSelector((state) => state.users);
    
      useEffect(() => {
        setIsLoadingUsers(true);
        dispatch(fetchUsers())
          .unwrap()
          .catch((err) => setLoadingUsersError(err))
          .finally(() => setIsLoadingUsers(false));
      }, []);
    
      let content;
    
      if (isLoadingUsers) {
        content = 'User is loading';
      } else if (loadingUsersError) {
        content = 'Load user fail';
      } else {
        content = data.map((user) => <div key={user.id}>{user.name}</div>);
      }
    
      return <div>{content}</div>;
    }
    

对比 与前两者:

在这里插入图片描述

前两者对于 Promise 处理的 reducers 还是属于手动的,使用 asyncThunk 则显得更加的规范化,不过保存的数据格式倒是一致的:

在这里插入图片描述

Redux Toolkit Query

RTKQ 有点对标 React Query,都是对 API 进行 cache 的解决方案,基础的实现方法如下:

  • slice/api:

    import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/dist/query/react';
    
    const userApi = createApi({
      reducerPath: 'users',
      // pre-configured fetch
      baseQuery: fetchBaseQuery({
        baseUrl: 'http://localhost:3005',
      }),
      endpoints(builder) {
        return {
          fetchUsers: builder.query({
            query: (user) => {
              return {
                url: '/users',
                method: 'GET',
              };
            },
          }),
        };
      },
    });
    
    export const { useFetchUsersQuery } = userApi;
    export { userApi };
    
  • store:

    import { configureStore } from '@reduxjs/toolkit';
    import logger from 'redux-logger';
    import { userApi } from './apis/userApi';
    
    export const store = configureStore({
      reducer: {
        [userApi.reducerPath]: userApi.reducer,
      },
      middleware: (getDefaultMiddleware) => {
        return getDefaultMiddleware().concat(userApi.middleware).concat(logger);
      },
    });
    
  • component:

    import React from 'react';
    import { useFetchUsersQuery } from '../store/apis/userApi';
    
    const UserRTK = () => {
      const { data, isFetching, error } = useFetchUsersQuery();
    
      let content;
    
      if (isFetching) {
        content = 'User is loading';
      } else if (error) {
        content = 'Load user fail';
      } else {
        content = data.map((user) => <div key={user.id}>{user.name}</div>);
      }
    
      return <div>{content}</div>;
    };
    
    export default UserRTK;
    

RTKQ 内部的管理流程大致如下:

在这里插入图片描述

middleware 显示 register 对应的 Query,接着就像 Thunk2 一样,内部对 pending/fulfilled/rejected 的状态进行管理,这里也能看到一个 internalSubscriptions 的存在。

这里只是一个简单的 Fetch,并不包含 CRUD,不过需要注意的是,拉数据是调用 builder.query 进行创建,而其他会对数据库产生影响的作用则使用 builder.mutate 进行创建。

除此之外,如果当前的 API 没有被触发,那么对应的数据并不会存在(因为没有被 cache),换言之如果我还有一个 product 的 slice/api,只是在用户页面没有调用 product 的 RTKQ,那么 product 就不会存在于 state 中。

RTKQ 只所以能够被称之为是一个对 query 的解决方案,也是因为通过一些配置就能够实现一些比较麻烦的功能:

在这里插入图片描述

而且 RTKQ 内部还有一个 tagging system,RTKQ 在 Fetch 阶段可以提供一个 tag,其内部会对这个 tag 进行缓存,在 mutation 阶段用户可以通过 invalidatesTags 对 tag 去无效华内部的 cache,当 RTKQ 察觉到 tag 的变化后,那么就会重新调用 query 去拉取最新的数据。

下一步大概就是研究一下 RTKQ 这个 tagging system 了,毕竟我还挺需要手动更新数据而不是让 RTKQ 去重新拉取数据(这个也是可以实现的)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/176955.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据分析-深度学习 Pytorch Day9

迁移学习通过利用数据、任务或模型之间的相似性&#xff0c;将在旧领域学习过的模型应用于新领域来求解新问题。生活中常用的“举一反三”、“照猫画虎”就很好地体现了迁移学习的思想。利用迁移学习的思想&#xff0c;可以将已有的一些训练好的模型&#xff0c;迁移到我们的任…

【Kotlin】泛型 ② ( 可变参数 vararg 关键字与泛型结合使用 | 使用 [] 运算符获取指定可变参数对象 )

文章目录一、可变参数 vararg 关键字与泛型结合使用二、使用 [] 运算符获取指定可变参数对象一、可变参数 vararg 关键字与泛型结合使用 如果 泛型类型 T 的参数 是 vararg 可变参数 , 则在接收 可变参数 时 , 需要使用 Array<out T> 类型 的变量进行接收 ; 参数为 vara…

分房管理系统Rose模型设计过程

文章目录 一、模型总体设计 1 创建系统的Use Case 视图 2 创建系统的 Logical 视图 3 创建系统的 Class 框图 4 创建系统的 StateChart 框图 5 创建系统的 Activity 框图 二、软件模块结构图设计 1 根据系统功能进行第一级分解 2 完成第二级分解 3 完成第三级分解 4 整合得到完…

从零开始的python基础教程

一、Getting Started 如果使用的是mac或linux系统&#xff0c;需要输入 python3 比如运行 python3 app.py 可以直接在终端的>>>符号后执行python代码 print("*" * 10) 1、实现 在相关终端当运行“python”为CPython&#xff0c;这是python的默认实现 Jy…

大数据--spark

什么是SparkApache Spark 的架构基础是弹性分布式数据集 (RDD)&#xff0c;这是一种只读的多组数据项&#xff0c;分布在机器集群上&#xff0c;以容错方式维护。[2] Dataframe API 作为 RDD 之上的抽象发布&#xff0c;随后是 Dataset API。在 Spark 1.x 中&#xff0c;RDD 是…

39-剑指 Offer 41. 数据流中的中位数

题目 如何得到一个数据流中的中位数&#xff1f;如果从数据流中读出奇数个数值&#xff0c;那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值&#xff0c;那么中位数就是所有数值排序之后中间两个数的平均值。 例如&#xff0c; [2,3,4] 的中位…

50个常用的 Numpy 函数详解

目录 一、创建数组 1、Array 2、Linspace 3、Arange 4、Uniform 5、Random.randint 6、Random.random 7、Logspace 8、zeroes 9、ones 10、full 11、Identity 二、数组操作 12、min 13、max 14、unique 15、mean 16、medain 17、digitize 18、reshape 19、…

详解1247:河中跳房子(二分经典例题)

1247&#xff1a;河中跳房子【题目描述】每年奶牛们都要举办各种特殊版本的跳房子比赛&#xff0c;包括在河里从一个岩石跳到另一个岩石。这项激动人心的活动在一条长长的笔直河道中进行&#xff0c;在起点和离起点L远 (1 ≤ L≤ 1,000,000,000) 的终点处均有一个岩石。在起点和…

《Unity Shader 入门精要》第6章 Unity 中的基础光照

第6章 Unity 中的基础光照 6.1 我们是如何看到这个世界的 通常来说我们要模拟真实的光照环境来生成一张图像&#xff0c;需要考虑3种物理现象&#xff1a; 首先&#xff0c;光线从光源&#xff08;light source&#xff09;中被发射出来然后&#xff0c;光线和场景中的一些物…

JavaScript while 循环

文章目录JavaScript while 循环while 循环do/while 循环比较 for 和 while笔记列表JavaScript while 循环 只要指定条件为 true&#xff0c;循环就可以一直执行代码块。 while 循环 while 循环会在指定条件为真时循环执行代码块。 语法 while (条件) {需要执行的代码 }本例中…

Redis内部的阻塞式操作以及应对方法

Redis之所以被广泛应用&#xff0c;很重要的一个原因就是它支持高性能访问&#xff0c;也正因为这样&#xff0c;我们必须要重视所有可能影响Redis性能的因素&#xff0c;不仅要知道具体的机制&#xff0c;尽可能避免异常的情况出现&#xff0c;还要提前准备好应对异常的方案。…

MySQL进阶篇之索引2

02、索引 前四节内容&#xff1a;https://blog.csdn.net/kuaixiao0217/article/details/128753999 2.5、SQL性能分析 2.5.1、查看执行频次 1、SQL执行频率 MySQL客户端连接成功后&#xff0c;通过show [session|global] status命令可以提供服务器状态信息。 通过如下指令…

Computer architecture Cyber security Quantum computing交友

如果您也是computer architecture方向的博士硕士&#xff0c;希望交个朋友&#xff0c;欢迎后台私信。 当然&#xff0c;如果您也是 Cyber SecurityQuantum ComputingHigh Performance Computing 方向的博士硕士&#xff0c;想要交流&#xff0c;也可以私信。

学习记录669@项目管理之项目合同管理

有效合同原则 有效合同应具备以下特点: (1)签订合同的当事人应当具有相应的民事权利能力和民事行为能力。 (2)意思表示真实。 (3)不违反法律或社会公共利益 与有效合同相对应&#xff0c;需要避免无效合同。无效合同通常需具备下列任一情形: (1)一方以欺诈、胁迫的手段订立合…

【模拟CMOS集成电路】电路失调与CMRR—— 随机失调与系统失调分析(1)

电路失调与CMRR—— 随机失调与系统失调分析&#xff08;1&#xff09;前言1.1失调1.2失调电路模型1.2.1随机失调电路模型&#xff08;1&#xff09;电阻失配&#xff08;2&#xff09;跨导失配&#xff08;3&#xff09;电流镜的随机失调1.2.2系统失调前言 本文主要内容是失调…

深入剖析JVM垃圾收集器

文章目录前言1、新生代垃圾收集器1.1、Serial1.2、ParNew1.3、Parallel Scavenge2、老年代垃圾收集器2.1、Serial Old2.2、Parallel Old2.3、CMS&#xff08;Concurrent Mark Sweep&#xff09;3、全堆垃圾收集器3.1、Garbage First&#xff08;G1&#xff09;前言 参考资料&am…

ConfigurationProperties将配置绑定到bean的过程分析

概述 ConfigurationProperties是一个大家常用的注解。有一些系统配置&#xff0c;经常放在yml中&#xff0c;然后通过spring注入到bean中。 一般这些配置都是通过在spring生命周期的某一个环节&#xff0c;将属性注入进去的。 ConfigurationProperties就是利用了org.springf…

AC500 基于 Profinet 通讯连接变频器

硬件连接 使用 PM583-ETH 作为 Profinet 通讯的主站&#xff0c;ACS800 变频器 RETA-02 作为 Profinet 通讯的从站 2 ABB 变频器设置 以安装有 RETA-02 总线适配器的 ACS800 变频器为例&#xff0c;参照下表进行参数设定。详 细内容请参考变频器手册和 RETA-02 用户手册。表中…

Python 超强命令行解析工具 argparse !

在工作中&#xff0c;我们经常需要从命令行当中解析出指定的参数&#xff0c;而 Python 也提供了相应的标准库来做这件事情&#xff0c;比如 sys, optparse, getopt, argparse。这里面功能最强大的莫过于 argparse&#xff0c;下面就来看看它用法。import argparse# 使用 argpa…

计算机视觉OpenCv学习系列:第七部分、图像操作-3

第七部分、图像操作-3第一节、图像统计信息1.像素值统计2.函数支持说明3.代码练习与测试第二节、图像直方图1.图像直方图定义2.直方图函数3.代码练习与测试第三节、图像直方图均衡化1.直方图均衡化2.直方图均衡化函数3.代码练习与测试学习参考第一节、图像统计信息 1.像素值统…