815 lines
21 KiB
Markdown
Executable File
815 lines
21 KiB
Markdown
Executable File
# Redux
|
||
|
||
> 状态管理
|
||
> 适合大型项目
|
||
> 类似的还有zustand, mobx, mobx-react-lite
|
||
|
||
## hello redux
|
||
|
||
```sh
|
||
npm install react-redux
|
||
```
|
||
|
||
```html
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>Document</title>
|
||
</head>
|
||
<body>
|
||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/redux/4.1.2/redux.min.js"></script>
|
||
<div>
|
||
<button id="sub">减少</button>
|
||
<span id="count">1</span>
|
||
<button id="add">增加</button>
|
||
</div>
|
||
<script>
|
||
const subBtn = document.getElementById('sub')
|
||
const addBtn = document.getElementById('add')
|
||
const countSpan = document.getElementById('count')
|
||
|
||
/** 网页中使用redux
|
||
* 1. 引入redux包
|
||
* 2. 创建reducer整合函数
|
||
* 3. 通过reducer对象创建store
|
||
* 4. 对store中的state进行订阅
|
||
* 5. 通过dispatch派发state的操作指令
|
||
* */
|
||
|
||
/**
|
||
* state 当前的state, 根据这个state生成新的state
|
||
* action 是一个对象, 保存操作的信息
|
||
*/
|
||
function reducer(state={count:0}, action){
|
||
switch (action.type){
|
||
case 'Add':
|
||
return {...state, count:state.count +1}
|
||
case 'Sub':
|
||
return {...state, count:state.count -1}
|
||
case 'Add_N':
|
||
return {...state, count:state.count +action.payload}
|
||
default:
|
||
return state
|
||
}
|
||
}
|
||
/**
|
||
* 第一个参数是派发器,第二个参数是初始值(也可以在reducer设置初始值)
|
||
*/
|
||
const store = Redux.createStore(reducer, {count:1})
|
||
// 订阅, 其实就是回调, 发生变化的时候执行
|
||
store.subscribe(()=>{
|
||
// console.log(store.getState())
|
||
countSpan.innerText = store.getState().count
|
||
})
|
||
|
||
let count = 1
|
||
|
||
subBtn.addEventListener('click',()=>{
|
||
// count--
|
||
// countSpan.innerText = count
|
||
store.dispatch({type: 'Sub'})
|
||
})
|
||
addBtn.addEventListener('click',()=>{
|
||
// count++
|
||
// countSpan.innerText = count
|
||
store.dispatch({type: 'Add'})
|
||
})
|
||
</script>
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
## Redux Toolkit (RTK)
|
||
|
||
[Quick Start | Redux Toolkit](https://redux-toolkit.js.org/tutorials/quick-start)
|
||
|
||
```
|
||
问题:
|
||
1.如果state过干复杂,将会非常难以维护
|
||
- 可以通过对state 分组为解次这个向题,创建多个reducer,然后將其合并为一个
|
||
2.state每次操作时,都需要对state进行复制,然后再去修改
|
||
3.case后边的常量维护起来会比较麻烦
|
||
```
|
||
|
||
```bash
|
||
# 安装
|
||
npm install react-redux @reduxjs/toolkit -S
|
||
```
|
||
|
||
```js
|
||
/**
|
||
* createSlice:创建reducer(归纳函数)切片
|
||
* - 名字, 用来自动生成action.type
|
||
* - 初始值
|
||
* - reducers: 放更新state的方法
|
||
* - 方法有两个参数: state和action
|
||
* state是一个代理对象,可以直接修改
|
||
*
|
||
* 切片会自动生成actions, 打印actions可以发现存储多个函数:actionCreator() action创建器
|
||
*
|
||
* 调用action创建器后会自动创建action对象
|
||
* action对象的结构 {type: 'name/函数名', payload: '函数的参数'}
|
||
*
|
||
* 创建store: configureStore(option)
|
||
* - {reducer: {名字:切片的reducer}}
|
||
*/
|
||
```
|
||
|
||
示例:
|
||
```js
|
||
// @/store/index.js
|
||
// 创建store
|
||
import { createSlice, configureStore } from '@reduxjs/toolkit'
|
||
// 初始值
|
||
const initialState = {
|
||
name: '唐僧',
|
||
age: 30,
|
||
gender: '男',
|
||
address: '长安',
|
||
}
|
||
// 创建切片, 切片会自动生成action和reducer
|
||
export const stuSlice = createSlice({
|
||
name: 'stu',
|
||
initialState,
|
||
reducers: {
|
||
setName: (state, action) => {
|
||
state.name = action.payload
|
||
},
|
||
setAge: (state, action) => {
|
||
state.age = action.payload
|
||
},
|
||
},
|
||
})
|
||
|
||
console.log(stuSlice.actions)
|
||
// actionCreator() action创建器
|
||
|
||
export const { setName, setAge } = stuSlice.actions
|
||
// 把action创建器暴露出去, 代替原来手写的{type,action}
|
||
|
||
const nameAction = setName('猪八戒')
|
||
console.log(nameAction)
|
||
// {type: 'stu/stuName', payload: '猪八戒'}
|
||
// 调用action创建器生成action对象
|
||
|
||
const store = configureStore({
|
||
reducer: {
|
||
student: stuSlice.reducer,
|
||
// reducer也是切片自动创建的
|
||
// 这里的student名字在引入store的时候要用到,切片可能有很多个,用名字取其中一个
|
||
},
|
||
})
|
||
console.log(store)
|
||
export default store
|
||
```
|
||
|
||
怎么使用:
|
||
|
||
```
|
||
// 1. 用redux提供的Provider把整个app包起来
|
||
// 和react自带的Provider的区别是这里传入的是store
|
||
```
|
||
|
||
```js
|
||
// @/index.js
|
||
|
||
import { Provider } from 'react-redux';
|
||
import store from './store'
|
||
|
||
// ...
|
||
root.render(
|
||
<React.StrictMode>
|
||
<Provider store={store}>
|
||
<App />
|
||
</Provider>
|
||
</React.StrictMode>
|
||
)
|
||
//...
|
||
```
|
||
|
||
```
|
||
// 2.在组件中使用
|
||
// 通过useSelector()钩子获取state, 通过useDispatch()获取派发器对象
|
||
// 修改state的时候,派发器传入切片自动生成的action创建器
|
||
// action创建器里的值是要修改的值
|
||
// 当然也可以手动传如action:格式 {type: 'stu/stuName', payload: '猪八戒'},但不推荐,容易出错
|
||
```
|
||
|
||
```js
|
||
// @/app.js
|
||
import { useSelector, useDispatch } from 'react-redux'
|
||
import {useState} from 'react'
|
||
import {setName} from './store'
|
||
import './App.css'
|
||
|
||
function App() {
|
||
// 通过useSelector()钩子获取state
|
||
// 默认state是所有的,可以只取一部分
|
||
const stu = useSelector(state => state.student)
|
||
// 这里的名字是configureStore参数里reducer对应的名字
|
||
// 通过useDispatch()获取派发器对象
|
||
const dispatch = useDispatch()
|
||
|
||
const [inputName, setInputName] = useState('')
|
||
const nameChangeHandler = (e)=>{
|
||
e.preventDefault()
|
||
setInputName(e.target.value)
|
||
}
|
||
const saveNameHandler = ()=>{
|
||
// dispatch传入切片自动生成的action
|
||
dispatch(setName(inputName))
|
||
}
|
||
|
||
return (
|
||
<div className="App">
|
||
<p>姓名: {stu.name}</p>
|
||
<p>年龄: {stu.age}</p>
|
||
<p>性别: {stu.gender}</p>
|
||
<p>地址: {stu.address}</p>
|
||
<p>
|
||
<input type="text" value={inputName} onChange={e=>nameChangeHandler(e)}/>
|
||
<button onClick={saveNameHandler}>修改名字</button>
|
||
</p>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default App
|
||
```
|
||
|
||
### 拆分模块
|
||
|
||
实际使用不会把切片都写在index里, 一般拆分开, 稍微修改下上面的代码
|
||
|
||
```js
|
||
// 新建@/store/stuSlice.js
|
||
import { createSlice } from '@reduxjs/toolkit'
|
||
|
||
const initialState = {
|
||
name: '唐僧',
|
||
age: 30,
|
||
gender: '男',
|
||
address: '长安',
|
||
}
|
||
|
||
export const stuSlice = createSlice({
|
||
name: 'stu',
|
||
initialState,
|
||
reducers: {
|
||
setName: (state, action) => {
|
||
state.name = action.payload
|
||
},
|
||
setAge: (state, action) => {
|
||
state.age = action.payload
|
||
},
|
||
},
|
||
})
|
||
|
||
export const { setName, setAge } = stuSlice.actions
|
||
|
||
export const {reducer:stuReducer} = stuSlice
|
||
```
|
||
|
||
```js
|
||
// @/store/index.js也要改下
|
||
import { configureStore } from '@reduxjs/toolkit'
|
||
import {stuReducer} from './stuSlice'
|
||
|
||
const store = configureStore({
|
||
reducer: {
|
||
student: stuReducer,
|
||
},
|
||
})
|
||
export default store
|
||
```
|
||
|
||
```js
|
||
// 引入的部分也要改下
|
||
// @/app.js
|
||
import { useSelector, useDispatch } from 'react-redux'
|
||
import {setName as setStuName} from './store/stuSlice'
|
||
// ...
|
||
const {student:stu} = useSelector(state => state)
|
||
|
||
const dispatch = useDispatch()
|
||
|
||
const saveNameHandler = ()=>{
|
||
// dispatch传入切片自动生成的action
|
||
dispatch(setName(inputName))
|
||
}
|
||
|
||
//...
|
||
```
|
||
|
||
## RTKQ
|
||
|
||
[RTK Query | Redux Toolkit](https://redux-toolkit.js.org/tutorials/rtk-query)
|
||
|
||
> Q: query, 简化在 Web 应用程序中加载数据的常见情况
|
||
|
||
### example
|
||
|
||
#### 1 . 创建模块
|
||
|
||
```js
|
||
// 创建rtkq中的api对象,会自动根据各种方法生成对应的钩子函数
|
||
// 参数:
|
||
// - reducerPath: 标识
|
||
// - baseQuery: 指定查询的基本信息, 发送请求使用的工具
|
||
// - endpoints: 指定api的各种功能, build: 请求的构建器
|
||
// - query 查询的方法,指定请求的路径
|
||
// - mutation 更新的方法
|
||
// - (可选)transformResponse 对响应数据进行处理
|
||
|
||
// 导出钩子命名规则: getStuList => useGetStuListQuery use表示钩子,Query表示查询方法
|
||
```
|
||
|
||
```js
|
||
// src/store/stuApi.js
|
||
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
|
||
|
||
export const stuApi = createApi({
|
||
reducerPath: 'stuApi',
|
||
baseQuery: fetchBaseQuery({ baseUrl: 'http://localhost:1337/api/' }),
|
||
endpoints: (build) => ({
|
||
getStuList: build.query({
|
||
query: () => 'students',
|
||
// transformResponse对响应数据进行处理
|
||
transformResponse(res){return res.data}
|
||
}),
|
||
getStuInfoById: build.query({
|
||
query: (id) => `students/${id}`, // 接收路径参数
|
||
}),
|
||
updateStuById: build.mutation({
|
||
query: ({ id, stu }) => {
|
||
return {
|
||
url: `students/${id}`,
|
||
method: 'put',
|
||
body: { data: stu },
|
||
}
|
||
},
|
||
}),
|
||
}),
|
||
})
|
||
|
||
export const {useGetStuListQuery} = stuApi
|
||
```
|
||
|
||
#### 2 . 配置 store
|
||
|
||
```js
|
||
// src/store/index.js
|
||
import { configureStore } from '@reduxjs/toolkit'
|
||
import { stuApi } from './stuApi'
|
||
|
||
// 添加到store
|
||
export const store = configureStore({
|
||
reducer: {
|
||
// api对象的路径名当store的标识
|
||
// api对象的reducer自动生成的
|
||
[stuApi.reducerPath]: stuApi.reducer,
|
||
},
|
||
// 添加中间件用来实现缓存, 失效, 轮寻
|
||
// 必须添加
|
||
middleware: getDefaultMiddleware => {
|
||
return getDefaultMiddleware().concat(stuApi.middleware)
|
||
},
|
||
})
|
||
```
|
||
|
||
#### 3 . 注入 store
|
||
|
||
```jsx
|
||
// src/main.jsx
|
||
import React from 'react'
|
||
import ReactDOM from 'react-dom/client'
|
||
import { Provider } from 'react-redux'
|
||
import App from './App'
|
||
import './index.css'
|
||
import store from './store'
|
||
|
||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||
<Provider store={store}>
|
||
<App />
|
||
</Provider>
|
||
)
|
||
```
|
||
|
||
#### 4 . 使用 api
|
||
|
||
```jsx
|
||
import { useState } from 'react'
|
||
import './App.css'
|
||
import { useGetStuListQuery } from './store/stuApi'
|
||
|
||
function App() {
|
||
// 调用钩子查询数据
|
||
// 钩子返回一个对象,包括请求过程中相关的数据
|
||
const obj = useGetStuListQuery()
|
||
console.log(obj)
|
||
const { isLoading, isSuccess, data} = obj
|
||
|
||
return (
|
||
<div className="App">
|
||
{isLoading && <p>--数据正在加载--</p>}
|
||
{isSuccess &&
|
||
data.map(item=>{
|
||
return <p key={item.id}>
|
||
姓名: {item.attributes.name} |
|
||
性别: {item.attributes.gender==='male'?'男':'女'} |
|
||
年龄: {item.attributes.age} |
|
||
地址: {item.attributes.address}
|
||
</p>
|
||
})
|
||
}
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default App
|
||
```
|
||
|
||
### `keepUnusedDataFor`:缓存功能
|
||
|
||
>rtkq会查询数据后会默认缓存60秒, 在期间不重复发请求而是使用缓存
|
||
|
||
设置缓存时间`keepUnusedDataFor`
|
||
|
||
```js
|
||
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
|
||
|
||
const api = createApi({
|
||
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
|
||
endpoints: (build) => ({
|
||
getPosts: build.query({
|
||
query: () => 'posts',
|
||
keepUnusedDataFor: 5, // 单位是秒,0为不缓存
|
||
}),
|
||
}),
|
||
})
|
||
```
|
||
|
||
### useQuery返回的对象
|
||
|
||
```js
|
||
/**
|
||
* refetch: 用来重新加载数据的函数
|
||
* status: 字符串,请求的状态
|
||
* isFetching: boolean,是否正在加载
|
||
* isLoading: boolean,数据是否第一次加载,调用refetch不会改变状态
|
||
* isSuccess: boolean,请求是否成功
|
||
* isError: boolean,是否出错
|
||
* isUninitialized: boolean,是否还未开始发送
|
||
* data: 最新的数据
|
||
* currentData: 当前参数的最新数据,和data的区别:当参数发生变化的时候,最开始保存的是undefined
|
||
*/
|
||
```
|
||
|
||
### useQuery传参
|
||
|
||
```js
|
||
// useQuery可以接收一个对象作为第二个参数,对请求进行配置
|
||
const { isFetching, isSuccess,isError, data } = useGetStuInfoByIdQuery(props.id, {
|
||
selectFromResult: result => result, // 对返回进行处理
|
||
pollingInterval: 0, // 轮寻间隔,单位毫秒.0表示不轮寻
|
||
skip:!props.id, // 是否跳过,默认false
|
||
refetchOnMountOrArgChange: false, // 设置是否每次组件挂载都重新加载数据,也可以传时间,单位是秒.例如设置2,表示两秒内,不重新加载
|
||
refetchOnFocus: false, // 页面获取焦点时是否重新加载数据,例如切换浏览器标签
|
||
refetchOnReconnect: false, // 网络恢复连接重新加载数据
|
||
// refetchOnFocus和refetchOnReconnect 需要额外在'@/store/index.js'里添加
|
||
// 1. import { setupListeners } from '@reduxjs/toolkit/query'
|
||
// 2. setupListeners(store.dispatch)
|
||
})
|
||
```
|
||
|
||
### useMutation
|
||
|
||
#### 1 . 创建模块
|
||
|
||
```js
|
||
// build.mutation 更新的方法
|
||
// return的是一个对象,包括url, method, body
|
||
// 导出钩子同样有规则, use前缀表示钩子, Mutation表示更新
|
||
```
|
||
|
||
```js
|
||
// src/store/stuApi.js
|
||
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
|
||
|
||
export const stuApi = createApi({
|
||
reducerPath: 'stuApi',
|
||
baseQuery: fetchBaseQuery({ baseUrl: 'http://localhost:1337/api/' }),
|
||
endpoints: build => ({
|
||
// 删除学生信息
|
||
delStuById: build.mutation({
|
||
query: id => {
|
||
// 如果发送的不是get请求,要返回一个对象,并配置信息
|
||
return {
|
||
url: `students/${id}`,
|
||
method: 'delete',
|
||
}
|
||
},
|
||
}),
|
||
// 添加学生
|
||
addStu: build.mutation({
|
||
query: stu => {
|
||
// post请求包含body属性
|
||
return {
|
||
url: 'students',
|
||
method: 'post',
|
||
body: { data: stu },
|
||
}
|
||
},
|
||
}),
|
||
// 更新学生信息
|
||
updateStuById: build.mutation({
|
||
query: ({ id, stu }) => {
|
||
return {
|
||
url: `students/${id}`,
|
||
method: 'put',
|
||
body: { data: stu },
|
||
}
|
||
},
|
||
}),
|
||
}),
|
||
})
|
||
|
||
export const {
|
||
useDelStuByIdMutation,
|
||
useAddStuMutation,
|
||
useUpdateStuByIdMutation,
|
||
} = stuApi
|
||
```
|
||
|
||
#### 2 . 配置 store
|
||
|
||
```jsx
|
||
import { configureStore } from '@reduxjs/toolkit'
|
||
import { setupListeners } from '@reduxjs/toolkit/query'
|
||
import { stuApi } from './stuApi'
|
||
|
||
// 添加到store
|
||
const store = configureStore({
|
||
reducer: {
|
||
// api对象的路径名当store的标识
|
||
// api对象的reducer自动生成的
|
||
[stuApi.reducerPath]: stuApi.reducer,
|
||
},
|
||
// 添加中间件用来实现缓存, 失效, 轮寻
|
||
// 必须添加
|
||
// 多个中间件, contat(a,b,c)
|
||
middleware: getDefaultMiddleware => {
|
||
return getDefaultMiddleware().concat(stuApi.middleware)
|
||
},
|
||
})
|
||
|
||
setupListeners(store.dispatch)
|
||
|
||
export default store
|
||
```
|
||
|
||
#### 3 . 注入 store
|
||
|
||
```jsx
|
||
// src/main.jsx
|
||
import React from 'react'
|
||
import ReactDOM from 'react-dom/client'
|
||
import { Provider } from 'react-redux'
|
||
import App from './App'
|
||
import './index.css'
|
||
import store from './store'
|
||
|
||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||
<Provider store={store}>
|
||
<App />
|
||
</Provider>
|
||
)
|
||
```
|
||
|
||
#### 4 . 使用 api
|
||
|
||
```js
|
||
// useMutation和useQuery不一样,返回的是数组,第一个是触发器,第二个是结果
|
||
// result的结构类似useQuery返回
|
||
// 触发器返回的res也是一个 promise对象
|
||
```
|
||
|
||
```jsx
|
||
import {useDelStuByIdMutation} from '@/store/stuApi'
|
||
|
||
// ...
|
||
|
||
const [delStu, {isLoading:delLoading}] = useDelStuByIdMutation()
|
||
const delStuHandler = () => {delStu(id)} // id是从props中解构得到的
|
||
|
||
// ...
|
||
```
|
||
|
||
### useMutation 返回的 result对象
|
||
|
||
```js
|
||
/**
|
||
* isError: boolean
|
||
* isLoading: boolean
|
||
* isSuccess: boolean
|
||
* isUninitialized: boolean
|
||
* originalArgs:
|
||
* reset: fn
|
||
* status: string
|
||
*/
|
||
```
|
||
|
||
### 使数据失效: 数据标签
|
||
|
||
> 举例: 更新某个学生的信息, 保存成功后, 要让学生列表的组件重渲染
|
||
|
||
```js
|
||
// 1. 设置有哪些标签类型 tagTypes
|
||
// 2. 需要更新的api功能设置 providesTags 提供标签
|
||
// 3. 触法更新的api功能设置 invalidatesTags 使标签失效
|
||
```
|
||
|
||
```js
|
||
// src/store/stuApi.js
|
||
/**
|
||
* 1. getStuList的标签是'stuList', addStu可以让所有的'stuList'标签失效,从而触发getStuList重新加载数据, 组件重渲染
|
||
* 标签可以有多个
|
||
* 2. 需要更颗粒化控制, 例如id为1的学生更新了信息, 只需要下次查询id为1的学生不使用缓存, 其他id如果有缓存就使用缓存
|
||
* providesTags属性设置为函数,函数返回一个数组,数组里放对象,对象包括type属性和id,id来自query传参的id
|
||
* invalidatesTags同样属性设置为函数,函数返回一个数组,数组里放对象,对象包括type属性和id,id来自query传参的id
|
||
**/
|
||
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
|
||
|
||
export const stuApi = createApi({
|
||
reducerPath: 'stuApi',
|
||
baseQuery: fetchBaseQuery({ baseUrl: 'http://localhost:1337/api/' }),
|
||
tagTypes: ['stuList','stuInfo'], // 指定api中的标签类型--type
|
||
endpoints: build => ({
|
||
// 获取学生列表
|
||
getStuList: build.query({
|
||
query: () => 'students',
|
||
// transformResponse对响应数据进行处理
|
||
transformResponse(res) {
|
||
return res.data
|
||
},
|
||
providesTags: ['stuList'] // 当标签失效的时候,触发数据加载. 这里的标签可以有多个,其中一个失效,就重新获取
|
||
}),
|
||
// 根据id获取学生信息
|
||
getStuInfoById: build.query({
|
||
query: id => `students/${id}`,
|
||
transformResponse(res) {
|
||
return res.data.attributes
|
||
},
|
||
keepUnusedDataFor: 30, // 设置缓存时间,单位秒,0是不缓存
|
||
providesTags: (result,error,id)=>{
|
||
// 设置tag更加详细,可以让某个type+id的失效,而不是整个type
|
||
// result: 请求返回的信息
|
||
// error: 错误信息
|
||
// id: query接收的id
|
||
return [{type: 'stuInfo',id}]
|
||
}
|
||
}),
|
||
// 删除学生信息
|
||
delStuById: build.mutation({
|
||
query: id => {
|
||
// 如果发送的不是get请求,要返回一个对象,并配置信息
|
||
return {
|
||
url: `students/${id}`,
|
||
method: 'delete',
|
||
}
|
||
},
|
||
}),
|
||
// 添加学生
|
||
addStu: build.mutation({
|
||
query: stu => {
|
||
return {
|
||
url: 'students',
|
||
method: 'post',
|
||
body: { data: stu },
|
||
}
|
||
},
|
||
invalidatesTags: ['stuList'] // 使标签失效
|
||
}),
|
||
// 更新学生信息
|
||
updateStuById: build.mutation({
|
||
query: ({ id, stu }) => {
|
||
return {
|
||
url: `students/${id}`,
|
||
method: 'put',
|
||
body: { data: stu },
|
||
}
|
||
},
|
||
invalidatesTags: (result,error,id)=>{
|
||
return ['stuList',{type:'stuInfo',id}] // 类似providesTags
|
||
}
|
||
}),
|
||
}),
|
||
})
|
||
|
||
export const {
|
||
useGetStuListQuery,
|
||
useGetStuInfoByIdQuery,
|
||
useDelStuByIdMutation,
|
||
useAddStuMutation,
|
||
useUpdateStuByIdMutation,
|
||
} = stuApi
|
||
```
|
||
|
||
上面的`tagTypes`可以简化成一个, 学生列表没有id, 可以给它指定一个独有的id, 如`[{type:'student',id:'list'}]`
|
||
|
||
```js
|
||
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
|
||
|
||
export const stuApi = createApi({
|
||
reducerPath: 'stuApi',
|
||
baseQuery: fetchBaseQuery({ baseUrl: 'http://localhost:1337/api/' }),
|
||
tagTypes: ['student'], // type改成只有一个
|
||
endpoints: build => ({
|
||
getStuList: build.query({
|
||
query: () => 'students',
|
||
transformResponse(res) {
|
||
return res.data
|
||
},
|
||
providesTags: [{type:'student',id:'list'}] // 指定id为'list'
|
||
}),
|
||
// 根据id获取学生信息
|
||
getStuInfoById: build.query({
|
||
query: id => `students/${id}`,
|
||
transformResponse(res) {
|
||
return res.data.attributes
|
||
},
|
||
keepUnusedDataFor: 30,
|
||
providesTags: (result,error,id)=>{
|
||
return [{type: 'student',id}]
|
||
}
|
||
}),
|
||
// 删除学生信息
|
||
delStuById: build.mutation({
|
||
query: id => {
|
||
return {
|
||
url: `students/${id}`,
|
||
method: 'delete',
|
||
}
|
||
},
|
||
invalidatesTags: [{type:'student',id:'list'}] // 使{type:'student',id:'list'}标签失效
|
||
}),
|
||
// 添加学生
|
||
addStu: build.mutation({
|
||
query: stu => {
|
||
return {
|
||
url: 'students',
|
||
method: 'post',
|
||
body: { data: stu },
|
||
}
|
||
},
|
||
invalidatesTags: [{type:'student',id:'list'}] // 使标签失效
|
||
}),
|
||
// 更新学生信息
|
||
updateStuById: build.mutation({
|
||
query: ({ id, stu }) => {
|
||
return {
|
||
url: `students/${id}`,
|
||
method: 'put',
|
||
body: { data: stu },
|
||
}
|
||
},
|
||
invalidatesTags: (result,error,id)=>{
|
||
return [{type:'student',id:'list'},{type:'student',id}] // 使多个标签失效
|
||
}
|
||
}),
|
||
}),
|
||
})
|
||
|
||
export const {
|
||
useGetStuListQuery,
|
||
useGetStuInfoByIdQuery,
|
||
useDelStuByIdMutation,
|
||
useAddStuMutation,
|
||
useUpdateStuByIdMutation,
|
||
} = stuApi
|
||
```
|
||
|
||
### 设置请求头
|
||
|
||
```js
|
||
// baseQuery 参数里, 除了 baseUrl,还有prepareHeaders用来设置请求头
|
||
// 第一个参数是默认headers, 第二个参数有获取state的方法
|
||
// 因为useSelect只能在函数组件或者自定义勾子中使用
|
||
```
|
||
|
||
```js
|
||
baseQuery: fetchBaseQuery({
|
||
baseUrl: 'http://localhost:1337/api/',
|
||
prepareHeaders: (headers, { getState }) => {
|
||
// 获取token
|
||
const token = getState().auth.token
|
||
if (token) {
|
||
headers.set('Authorization', `Bearer ${token}`)
|
||
}
|
||
return headers
|
||
},
|
||
}),
|
||
``` |