React--》Redux Toolkit的使用讲解
目录
Redux Toolkit
redux toolkit的基本使用
RTK代码模块化
RTK QUERY的使用
useQuery参数
Redux Toolkit
Redux Toolkit是Redux的工具包,简称RTK,可以帮助我们处理使用Redux过程中的重复性工作,简化Redux中的各种操作,当然博主也不可能将RTK讲解的面面俱到,看完本篇文章后还是推荐大家多多看一下 官方文档 ,无论是RTK还是Redux,在React中使用 react-redux 都是必不可少的,所以使用RTK依然需要安装两个包,如下:
npm install react-redux @reduxjs/toolkit
redux toolkit的基本使用
官方已经给出我们使用redux toolkit的基本示例了,如下图所示,在这里我结合官方给出的示例简单的说明并讲解一下函数的使用。
根据官方给出的基础案例,在src文件夹下新建文件夹store,然后新建index.jsx文件,并写出如下代码:
// 使用RTK创建store, createSlice 创建reducer的切片
import { configureStore, createSlice } from "@reduxjs/toolkit";
const stuSlice = createSlice({ // 需要一个配置对象作为参数,通过对象的不同属性来指定它的配置
name:'stu', // 用来自动生成 action 中的 type
initialState:{ // state的初始值
name:"张三",
age:18,
gender:"男",
address:"北京"
},
// 指定state的各种操作,直接在对象中添加方法
reducers:{
setName: state => { // state是一个代理对象,可直接修改
state.name = "李四"
},
setAge: state => {
state.age = 28
}
}
})
// 切片对象会自动的帮助我们生成action
export const { setName,setAge } = stuSlice.actions
// 创建store,用来创建store对象,需要一个配置对象作为参数
const store = configureStore({
reducer:{
student:stuSlice.reducer
}
})
export default store
要想在全局使用store文件中定义的数据和方法,需要在入口文件main.js文件中进行如下操作:
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import { Provider } from 'react-redux'
import store from './store'
ReactDOM.createRoot(document.getElementById('root')).render(
<Provider store={store}>
<App />
</Provider>
)
接下里通过调用redux相关api来获取数据和方法。
import { useSelector,useDispatch } from "react-redux"
import { setName,setAge } from "./store"
const App = () => {
// useSelector() 用来加载state中的数据
const student = useSelector(state => state.student)
// 通过useDispatch() 来获取派发器对象
const dispatch = useDispatch()
const changeName = () =>{
dispatch(setName())
}
const changeAge = () =>{
dispatch(setAge())
}
return (
<div>
<p>{student.name}--{student.age}--{student.gender}--{student.address}</p>
<button onClick={changeName}>修改名字</button>
<button onClick={changeAge}>修改年龄</button>
</div>
)
}
export default App
如果想通过传参的方式进行修改数据的话,可以采用以下这种方式:
RTK代码模块化
因为state的数据可能不止一种,将每一种数据都存放在store的一个文件中就会显得代码特别的臃肿,后期难以维护,为了便于今后的管理,可以对RTK代码进行模块化划分,也就是说,每一个数据都有单独的文件,最后所有的文件都整合到store文件夹下的index文件中,如下:
将我设置好的两个store数据单独抽离出去:
import { createSlice } from "@reduxjs/toolkit";
// 创建学生切片
const stuSlice = createSlice({ // 需要一个配置对象作为参数,通过对象的不同属性来指定它的配置
name:'stu', // 用来自动生成 action 中的 type
initialState:{ // state的初始值
name:"张三",
age:18,
gender:"男",
address:"北京"
},
// 指定state的各种操作,直接在对象中添加方法
reducers:{
setName: (state,action) => { // state是一个代理对象,可直接修改
state.name = action.payload
},
setAge: (state,action) => {
state.age = action.payload
}
}
})
// 切片对象会自动的帮助我们生成action
export const { setName,setAge } = stuSlice.actions
export const { reducer:stuReducer } = stuSlice
import { createSlice } from "@reduxjs/toolkit";
// 创建学校切片
const schoolSlice = createSlice({
name:"school",
initialState:{
name:'北京大学',
address:'北京市',
},
reducers:{
setName: (state,action) => {
state.name = action.payload
},
setAddress: (state,action) => {
state.address = action.payload
}
}
})
export const { setName,setAddress } = schoolSlice.actions
export const { reducer:schoolReducers } = schoolSlice
将抽离出去的数据都存放在store文件夹下的index.jsx文件中,如下:
// 使用RTK创建store,用于管理所有数据的文件
import { configureStore } from "@reduxjs/toolkit";
import { stuReducer } from "./student";
import { schoolReducers } from "./school";
// 创建store,用来创建store对象,需要一个配置对象作为参数
const store = configureStore({
reducer:{
student:stuReducer,
school:schoolReducers
}
})
export default store
在相关组件中调用该store中的数据:
import { useSelector,useDispatch } from "react-redux"
import { setName,setAge } from "./store/student"
import { setAddress,setName as setSchoolName } from "./store/school"
const App = () => {
// useSelector() 用来加载state中的数据
const { student,school } = useSelector(state => state)
// 通过useDispatch() 来获取派发器对象
const dispatch = useDispatch()
const changeName = () =>{
dispatch(setName("王五"))
}
const changeAge = () =>{
dispatch(setAge(30))
}
const changeSchoolName = () =>{
dispatch(setSchoolName("五道口职业技术学院"))
}
const changeSchoolAddress = () =>{
dispatch(setAddress("海淀区"))
}
return (
<div>
<h2>个人信息:</h2>
<p>{student.name}--{student.age}--{student.gender}--{student.address}</p>
<button onClick={changeName}>修改个人名字</button>
<button onClick={changeAge}>修改个人年龄</button>
<h2>学校信息:</h2>
<p>{school.name}--{school.address}</p>
<button onClick={changeSchoolName}>修改学校名字</button>
<button onClick={changeSchoolAddress}>修改学校地址</button>
</div>
)
}
export default App
可以看到上文进行抽离的代码给人的感觉是十分干练整洁的,十分便于维护,结果如下:
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),需要自己引入要使用的发送请求的工具。ok接下来开始讲解如何使用RTKQ,如下:
在store文件中新建一个word文件用于获取名言警句的接口,如下:
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
// 创建Api对象,createApi()用来创建RTKQ中的API对象
const wordApi = createApi({
reducerPath:'wordApi', // Api的标识,不能和其他的Api或reducer重复
baseQuery: fetchBaseQuery({ // 指定查询的基础信息,发送请求使用的工具
baseUrl:"https://api.uixsj.cn/"
}),
// build是请求的构造器,通过build来设置请求的相关信息
endpoints(build){
return {
getStudents:build.query({
query(){
// 用来指定请求的子路径
return 'hitokoto/get'
}
})
}
}
})
// Api对象创建后,对象中会根据各种方法自动是生成对应的钩子函数,通过这些钩子函数,可向服务器发送请求
// 钩子函数的命名规则 getStudents --> useGetStudentsQuery
export const { useGetStudentsQuery } = wordApi
export default wordApi
填写完接口文件后,在store文件夹下的index.jsx文件中进行引入:
import { configureStore } from "@reduxjs/toolkit";
import wordApi from "./word";
const store = configureStore({
reducer:{
[wordApi.reducerPath]:wordApi.reducer
},
// 添加一个中间件,这个中间件已自动生成了我们直接引入即可,中间件用来处理Api缓存
middleware:getDefaultMiddleware => {
return getDefaultMiddleware().concat(wordApi.middleware)
}
})
export default store
当然这里的话我们也需要在入口文件中进行store传入:
接下来开始使用我们创建的钩子函数进行相关数据的调用,如下:
currentData:undefined // 当前参数的最新数据
data:undefined // 最新的数据
isError:false // 布尔值,是否有错误
error:Error() // 对象,有错时才存在
isFetching:true // 布尔值,数据是否在加载
isLoading:true // 布尔值,数据第一次加载成功
isSuccess:false // 布尔值,请求是否成功
isUninitialized:false // 布尔值,请求是否还没有开始发送
refetch:f() // 一个函数,用来重新加载数据
status:"pending" // 字符串,请求的状态
因为我调用的接口没有data数据,数据在error中,这里的话需要我们在error进行数据的获取:
import { useGetStudentsQuery } from './store/word'
const App = () => {
// 调用Api查询数据,这个钩子函数它会返回一个对象作为返回值,请求过程中相关数据都在该对象中
const {isError,error,isLoading} = useGetStudentsQuery()
return (
<div>
{ isLoading && <p>数据加载中...</p> }
{isError && error.data}
</div>
)
}
export default App
如果接收的接口数据有很多,但自己只是想要其中的一小部分,可以通过以下方式解决:
useQuery参数
RTKQ给我们提供对接收到的数据进行自定义设置,如下:
import { useGetStudentsQuery } from './store/word'
const App = () => {
// 调用Api查询数据,这个钩子函数它会返回一个对象作为返回值,请求过程中相关数据都在该对象中
const result = useGetStudentsQuery(null,{
// useQuery可以接收一个对象作为第二个参数,通过该对象可以对请求进行配置
selectFromResult:result => { // 用来指定useQuery返回的结果
if(result.data === undefined){
result.data = '值被我修改了'
}
return result
},
pollingInterval:0, // 设置轮询的间隔(隔一段时间发起一次请求),单位毫秒,如果为0则不轮询
skip:false, // 设置是否跳过当前请求,默认为false
refetchOnMountOrArgChange:false, // 设置是否每次都重新加载数据,false正常使用缓存;true每次都重载数据;数字,数据缓存的时间(秒)
})
console.log(result)
const {isError,error,isLoading} = result
return (
<div>
{ isLoading && <p>数据加载中...</p> }
{isError && error.data}
</div>
)
}
还有两个参数设置需要在store进行数据监听才能使用,如下: