代码编织梦想

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 去重新拉取数据(这个也是可以实现的)。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_42938619/article/details/128754066

前端追梦人Redux Toolkit教程(简化redux的使用)-爱代码爱编程

一. 简介 该包是redux的工具集,旨在解决以下问题: store的配置复杂想让redux更加好用需要安装大量额外包redux要求写很多模板代码二.包含的api configureStore() 提供简化的配置选项和良好的默认值。它可以自动组合众多的reducers,添加用户提供的任何Redux中间件,默认情况下包括Redux -thunk(处理异

Redux - redux/toolkit-爱代码爱编程

简化Redux使用步骤, 及所需要的声明文件. 解决: 太多的样板文件、模版代码(action、reducer)复杂的配置 , 处理中间件state 更新麻烦 , 自行处理返回新的对象自带默认配置,中间件:redux-thunk\redux-devtools-extension安装 npm i @reduxjs/toolkit 示例

使用redux-toolkit简化react中redux的操作-爱代码爱编程

一、基本使用 1、官网地址 2、在项目中直接安装 npm install @reduxjs/toolkit react-redux 3、查看@reduxjs/toolkit的依赖包 其中自动集成了thunk处理异步的包 ... "dependencies": { "immer": "^9.0.1", "redux": "^4.0.

Redux流程分析 传统流程和redux-toolkit的使用-爱代码爱编程

redux官方给的两种库 传统Redux流程写法 react-redux Redux框架主要由Action、Reducer和Store三大元素组成。 action Action是一个普通对象,其中存在的type属性是必须的,用来表示Action的名称,type一般被定义为普通的字符串常量。为了方便管理,一般通过action creat

Redux Toolkit工具使用-爱代码爱编程

Redux Toolkit是Redux官方推出的工具集,因为Redux编写过程中会写很多的样板代码,对开发者不太友好,因此官方推出Redux Toolkit,Redux Toolkit就是对Redux的二次封装,用于高效 Redux 开发,使 Redux 的使用变得简单,下面用 Redux Toolkit 实现 TodoList 案例 npm i @re

使用 Redux Toolkit 和 RTK 查询创建 React 应用程序-爱代码爱编程

您是否曾经想将 Redux 与 React Query 提供的功能一起使用?现在,您可以使用 Redux Toolkit 及其最新添加的功能:RTK Query。 RTK Query 是一种高级数据获取和客户端缓存工具。它的功能类似于 React Query,但它的好处是直接与 Redux 集成。对于 API 交互,开发人员在使用 Redux 时通常会使

Redux Toolkit使用-爱代码爱编程

1、引入 yarn add @reduxjs/toolkit react-redux or npm install @reduxjs/toolkit react-redux 2、入口js文件引入store import React from "react"; import ReactDOM from "react-dom"; import rep

从零开始学React特别篇~Redux+Redux Toolkit介绍与使用-爱代码爱编程

前言 首先我原本是一个vue开发者,会有vuex的使用经验,虽然说可能上手会快,但是也有可能会被框架包袱所限制 为了写好今天这篇,看了特别多文档。 首先吐槽一下,最开始的时候我看的是这个文档 redux中文文档 我还想着这个中文文档真的是给人类看的吗?翻译成这样我还不如去看英文文档。每个字我都认识,连起来就看不懂了。 后面才知道上面那个不是官网,这

14.Redux-Toolkit-爱代码爱编程

Redux-Toolkit 一个官方提供用于Redux高效开发的、有想法的、功能齐全的工具包 #安装 npm i @reduxjs/toolkit #or yarn add @reduxjs/toolkit yarn add react-redux # 安装插件 yarn add redux-devtools -D # 安装调试工具,-D安装开发依赖

redux最佳实践「redux toolkit」_mr.指尖舞者的博客-爱代码爱编程

 首先你要有基本的Redux ,React-Reudx,中间件 的基础 如果没有基础,可以看下面两篇文章  redux详解     在项目中使用redux,react-redux 一,Redux Tooikit解决的问题  1.redux和react-redux存在问题 配置Redux Store并不简单。我们需要几个软件包来使Redux与R

你不了解的 @reduxjs/toolkit 中的createapi_shmily灬jj的博客-爱代码爱编程

背景 其实网上关于@reduxjs/toolkit的介绍很多了,但是我发现很少有关于createApi这个方法介绍的,这个功能却又特别强大,于是写下这篇文章 正文开始 // 先体验一下不用createApi时如何处理

细说 redux toolkit 1 : 重新回顾 redux_超悠閒的博客-爱代码爱编程

细说 Redux Toolkit 1 : 重新回顾 Redux 文章目录 细说 Redux Toolkit 1 : 重新回顾 Redux前言Redux 数据流 & 核心概念实现核心示例State、Reduc

redux toolkit超详细_devil__w的博客-爱代码爱编程

在认识redux-tookit 之前我们回顾一下react-redux React-Redux 安装 npm i react react-redux -D yarn add react react-re

react18中immer和redux toolkit的使用_月光晒了很凉快的博客-爱代码爱编程

文章目录 1. immer1.1 setState结合immer使用1.2 useState结合immer使用1.3 immer和redux集合 2. Redux Toolkit 1. immer 概

redux-爱代码爱编程

asyncSlice.ts import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' import { request } from '@/utils

redux使用详解(三)-爱代码爱编程

Redux 认识Redux Toolkit Redux Toolkit 是官方推荐的编写 Redux 逻辑的方法。 在前面我们学习Redux的时候应该已经发现,redux的编写逻辑过于的繁琐和麻烦。 并且代码通常分拆在多

react学习笔记四(redux和react-爱代码爱编程

文章目录 1. 知识铺垫 1.1 理解JavaScript纯函数 1.2 副作用概念的理解 1.3 纯函数的案例 1.4 纯函数的作用和优势 2