日期:2024年12月3日

RTK Query

RTK不仅帮助我们解决了state的问题,同时,它还为我们提供了RTK Query用来帮助我们处理数据加载的问题。RTK Query是一个强大的数据获取和缓存工具。在它的帮助下,Web应用中的加载变得十分简单,它使我们不再需要自己编写获取数据和缓存数据的逻辑。

Web应用中加载数据时需要处理的问题:

  1. 根据不同的加载状态显示不同UI组件
  2. 减少对相同数据重复发送请求
  3. 使用乐观更新,提升用户体验
  4. 在用户与UI交互时,管理缓存的生命周期

这些问题,RTKQ都可以帮助我们处理。首先,可以直接通过RTKQ向服务器发送请求加载数据,并且RTKQ会自动对数据进行缓存,避免重复发送不必要的请求。其次,RTKQ在发送请求时会根据请求不同的状态返回不同的值,我们可以通过这些值来监视请求发送的过程并随时中止。

使用

RTKQ已经集成在了RTK中,如果我们已经在项目中引入了RTK则无需再引入其余的模块。如果你不想使用RTKQ给我们提供的发送请求的方式(简单封装过的fetch),你还需要引入一下你要使用的发送请求的工具。

创建Api切片

RTKQ中将一组相关功能统一封装到一个Api对象中,比如:都是学生相关操作统一封装到StudentApi中,关于班级的相关操作封装到ClassApi中。接下来,我们尝试创建一个简单的Api,至于数据还是我们之前所熟悉的学生数据:

studentApi.js

import {createApi, fetchBaseQuery} from "@reduxjs/toolkit/dist/query/react";

export const studentApi = createApi({
reducerPath:'studentApi',
baseQuery:fetchBaseQuery({
baseUrl:'http://localhost:1337/api/'
}),
endpoints(build) {
return {
getStudents: build.query({
query() {
return 'students'
}
}),
}
}
});

export const {useGetStudentsQuery} = studentApi;

上例是一个比较简单的Api对象的例子,我们来分析一下,首先我们需要调用createApi()来创建Api对象。这个方法在RTK中存在两个版本,一个位于@reduxjs/toolkit/dist/query下,一个位于@reduxjs/toolkit/dist/query/react下。react目录下的版本会自动生成一个钩子,方便我们使用Api。如果不要钩子,可以引入query下的版本,当然我不建议你这么做。

createApi()需要一个配置对象作为参数,配置对象中的属性繁多,我们暂时介绍案例中用到的属性:

reducerPath

用来设置reducer的唯一标识,主要用来在创建store时指定action的type属性,如果不指定默认为api。

baseQuery

用来设置发送请求的工具,就是你是用什么发请求,RTKQ为我们提供了fetchBaseQuery作为查询工具,它对fetch进行了简单的封装,很方便,如果你不喜欢可以改用其他工具,这里暂时不做讨论。

fetchBaseQuery

简单封装过的fetch调用后会返回一个封装后的工具函数。需要一个配置对象作为参数,baseUrl表示Api请求的基本路径,指定后请求将会以该路径为基本路径。配置对象中其他属性暂不讨论。

endpoints

Api对象封装了一类功能,比如学生的增删改查,我们会统一封装到一个对象中。一类功能中的每一个具体功能我们可以称它是一个端点。endpoints用来对请求中的端点进行配置。

endpoints是一个回调函数,可以用普通方法的形式指定,也可以用箭头函数。回调函数中会收到一个build对象,使用build对象对点进行映射。回调函数的返回值是一个对象,Api对象中的所有端点都要在该对象中进行配置。

对象中属性名就是要实现的功能名,比如获取所有学生可以命名为getStudents,根据id获取学生可以命名为getStudentById。属性值要通过build对象创建,分两种情况:

查询:build.query({})

增删改:build.mutation({})

例如:

getStudents: build.query({
    query() {
        return 'students'
    }
}),

先说query,query也需要一个配置对象作为参数(又他喵的是配置对象)。配置对象里同样有n多个属性,现在直说一个,query方法。注意不要搞混两个query,一个是build的query方法,一个是query方法配置对象中的属性,这个方法需要返回一个子路径,这个子路径将会和baseUrl拼接为一个完整的请求路径。比如:getStudets的最终请求地址是:

http://localhost:1337/api/+students=http://localhost:1337/api/students

可算是介绍完了,但是注意了这个只是最基本的配置。RTKQ功能非常强大,但是配置也比较麻烦。不过,熟了就好了。

上例中,我们创建一个Api对象studentApi,并且在对象中定义了一个getStudents方法用来查询所有的学生信息。如果我们使用react下的createApi,则其创建的Api对象中会自动生成钩子函数,钩子函数名字为useXxxQuery或useXxxMutation,中间的Xxx就是方法名,查询方法的后缀为Query,修改方法的后缀为Mutation。所以上例中,Api对象中会自动生成一个名为useGetStudentsQuery的钩子,我们可以获取并将钩子向外部暴露。

export const {useGetStudentsQuery} = studentApi;

创建store对象

Api对象的使用有两种方式,一种是直接使用,一种是作为store中的一个reducer使用。store是我们比较熟悉的,所以先从store入手。

import {configureStore} from "@reduxjs/toolkit";
import {studentApi} from "./studentApi";

export const store = configureStore({
    reducer:{
        [studentApi.reducerPath]:studentApi.reducer
    },
    middleware:getDefaultMiddleware =>
        getDefaultMiddleware().concat(studentApi.middleware),
});

创建store并没有什么特别,只是注意需要添加一个中间件,这个中间件已自动生成了我们直接引入即可,中间件用来处理Api的缓存。

store创建完毕同样要设置Provider标签,这里不再展示。接下来,我们来看看如果通过studentApi发送请求。由于我们已经将studentApi中的钩子函数向外部导出了,所以我们只需通过钩子函数即可自动加载到所有的学生信息。比如,现在在App.js中加载信息可以这样编写代码:

import React from 'react';
import {useGetStudentsQuery} from './store/studentApi';

const App = () => {
    const {data, isFetching, isSuccess} = useGetStudentsQuery();

    return (
        <div>
            {isFetching && <p>数据正在加载...</p>}
            {isSuccess && data.data.map(item => <p key={item.id}>
                {item.attributes.name} --
                {item.attributes.age} --
                {item.attributes.gender} --
                {item.attributes.address}
            </p>)}
        </div>
    );
};

export default App;

直接调用useGetStudentsQuery()它会自动向服务器发送请求加载数据,并返回一个对象。这个对象中包括了很多属性:

  1. data – 最新返回的数据
  2. currentData – 当前的数据
  3. error – 错误信息
  4. isUninitialized – 如果为true则表示查询还没开始
  5. isLoading – 为true时,表示请求正在第一次加载
  6. isFetching 为true时,表示请求正在加载
  7. isSuccess 为true时,表示请求发送成功
  8. isError 为true时,表示请求有错误
  9. refetch 函数,用来重新加载数据

使用中可以根据需要,选择要获取到的属性值。写了这么多,也只写了一个Hello World。但是,良好的开端是成功的一半,这个理解了,后边的东西也就简单了!

4.8 12 投票数
文章评分
订阅评论
提醒
guest

7 评论
最旧
最新 最多投票
内联反馈
查看所有评论
Chin
2 年 前

自从了解了一些redux,已经越来越体会不到“状态管理”的感觉了(狗头

Dnzzk2
Dnzzk2
2 年 前
回复给  Chin

我们公司用的umi-request

Tony
Tony
2 年 前

很喜欢这样的教学文档,只提供一个入门,详细的需要自己去深啃

Allen
Allen
1 年 前

fetchBaseQuery 的 prepareHeaders headers设置header的时候会把原来的cors去掉,造成跨域

duzhe
duzhe
1 年 前

超哥啥时候讲讲TS 随着技术的精进 渐渐可以碰到一些ts代码了

超人只会飞
超人只会飞
1 年 前
回复给  duzhe

有同感啊 喜欢超哥的风格 希望继续这样的分享 也希望做个TS的视频

你好啊
你好啊
1 年 前
回复给  duzhe

尚硅谷 有 超哥的 Typescript教程哦!2020錄的,10小時,肯定堪用!

7
0
希望看到您的想法,请您发表评论x