RTK不仅帮助我们解决了state的问题,同时,它还为我们提供了RTK Query用来帮助我们处理数据加载的问题。RTK Query是一个强大的数据获取和缓存工具。在它的帮助下,Web应用中的加载变得十分简单,它使我们不再需要自己编写获取数据和缓存数据的逻辑。
Web应用中加载数据时需要处理的问题:
- 根据不同的加载状态显示不同UI组件
- 减少对相同数据重复发送请求
- 使用乐观更新,提升用户体验
- 在用户与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()它会自动向服务器发送请求加载数据,并返回一个对象。这个对象中包括了很多属性:
- data – 最新返回的数据
- currentData – 当前的数据
- error – 错误信息
- isUninitialized – 如果为true则表示查询还没开始
- isLoading – 为true时,表示请求正在第一次加载
- isFetching 为true时,表示请求正在加载
- isSuccess 为true时,表示请求发送成功
- isError 为true时,表示请求有错误
- refetch 函数,用来重新加载数据
使用中可以根据需要,选择要获取到的属性值。写了这么多,也只写了一个Hello World。但是,良好的开端是成功的一半,这个理解了,后边的东西也就简单了!
自从了解了一些redux,已经越来越体会不到“状态管理”的感觉了(狗头
我们公司用的umi-request
很喜欢这样的教学文档,只提供一个入门,详细的需要自己去深啃
fetchBaseQuery 的 prepareHeaders headers设置header的时候会把原来的cors去掉,造成跨域
超哥啥时候讲讲TS 随着技术的精进 渐渐可以碰到一些ts代码了
有同感啊 喜欢超哥的风格 希望继续这样的分享 也希望做个TS的视频
尚硅谷 有 超哥的 Typescript教程哦!2020錄的,10小時,肯定堪用!