Bootstrap

Redux使用详解(四)--RTK Query

RTKQ

createApi() 用来创建RTKQ中的API对象
RTKQ的所有功能都需要通过该对象来进行
createApi() 需要一个对象作为参数

reducerPath: Api的标识 不能和其他的Api或reducer重复
baseQuery: 指定查询的基础信息,发送请求使用的工具
endpoints: 用来指定Api中的各种功能,是一个方法,需要一个对象作为返回值
build: 是请求的构建器,通过build来设置请求的相关信息
transformResponse: 用来转换响应数据的格式

Api对象创建后,对象会根据各种方法自动的生成对应的钩子函数
通过这些钩子函数,可以来向服务器发送请求
钩子函数的命名规则 getStudents -> useGetStudentsQuery

下面是一个案例
studentApi.js

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

const studentApi = createApi({
	reducerPath: 'studentApi',
	baseQuery: fetchBaseQuery({
		baseUrl: 'http://,,,/api/'
	}),
	endpoints(build) {
		return {
			getStudents: build.query({
				query() {
					// 用来请求子路径
					return 'students'
				},
				transformResponse(baseQueryReturnValue, meta, arg){
					return baseQueryReturnValue.data
				}
			})	
		}
	}
})

export const {
	useGetStudentsQuery
} = studentApi

export default studentApi

index.js

import {congigureStore} from '@reduxjs/toolkit'
import studentApi from './studentApi'

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

export default store

app.js

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

const App = () => {

    // 调用Api查询数据
    // 这个钩子函数它会返回一个对象作为返回值,请求过程中相关数据都在该对象中存储
    const {data, isSuccess, isLoading} = useGetStudentsQuery(); // 调用Api中的钩子查询数据

    return (
        <div>
            {isLoading && <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;

根据id获取student

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

const studentApi = createApi({
	reducerPath: 'studentApi',
	baseQuery: fetchBaseQuery({
		baseUrl: 'http://,,,/api/'
	}),
	endpoints(build) {
		return {
			getStudents: build.query({
				query() {
					// 用来请求子路径
					return 'students'
				},
				transformResponse(baseQueryReturnValue, meta, arg){
					return baseQueryReturnValue.data
				}
			}),
			getStudentById:build.query({
				query(id) {
					return `students/${id}`
				},
				transformResponse(baseQueryReturnValue, meta, arg){
					return baseQueryReturnValue,data
				}
			})
		}
	}
})

export const {
	useGetStudentsQuery,
	useGetStudentByIdQuery
} = studentApi

export default studentApi

RTKQ的缓存

keepUnusedDataFor :设置数据缓存的时间 单位秒 默认60s

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

const studentApi = createApi({
	reducerPath: 'studentApi',
	baseQuery: fetchBaseQuery({
		baseUrl: 'http://,,,/api/'
	}),
	endpoints(build) {
		return {
			getStudents: build.query({
				query() {
					// 用来请求子路径
					return 'students'
				},
				transformResponse(baseQueryReturnValue, meta, arg){
					return baseQueryReturnValue.data
				}
			}),
			getStudentById:build.query({
				query(id) {
					return `students/${id}`
				},
				transformResponse(baseQueryReturnValue, meta, arg){
					return baseQueryReturnValue,data
				},
				keepUnuseDataFor:60,
			})
		}
	}
})

export const {
	useGetStudentsQuery,
	useGetStudentByIdQuery
} = studentApi

export default studentApi

useQuery的返回值

currentData: undefined 当前参数的最新数据
data: undefined 最新数据
isError: false 布尔值 是否有错误
error: Error() 对象 有错误时才存在
isFetrching: true 布尔值 数据是否在加载
isLoading: true 布尔值 数是否第一次加载
isSuccess: false 布尔值 请求是否成功
isUninitialized: false 布尔值 请求是否还没有开始发送
refetch: f() 一个函数 用来重新加载数据
status: ”pending“ 字符串 请求的状态

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

let num = 0;

const App = () => {

    const result = useGetStudentsQuery(num);
    console.log(result.data, result.currentData);
    const {data:stus, isSuccess, isLoading, refetch} = result; // 调用Api中的钩子查询数据

    return (
        <div>
            <button onClick={()=>refetch()}>刷新</button>
            <button onClick={()=>num++}>改变num</button>
            {isLoading && <p>数据加载中...</p>}
            {isSuccess && <StudentList stus={stus}/>}
        </div>
    );
};

export default App;

useQuery的参数

useQuery可以接收一个对象作为第二个参数 通过该对象可以对请求进行配置
selectFromResult 用来执行useQuery返回的结果
pollingInterval 设置轮询的间隔 单位毫秒 如果为0则表示不轮询
skip 设置是否跳过当前请求 默认false
refetchOnMountOrArgChange 设置是否每次都重新加载数据 false正常使用缓存 true每次都重载数据 数字 数据缓存的时间(秒)
refetchOnFocus 是否在重新获取焦点时重载数据
refetchOnReconnect 是否在重新连接后重载数据

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


const App = () => {

    const result = useGetStudentsQuery(null, {
         selectFromResult: result => {
             if (result.data) {
                 result.data = result.data.filter(item => item.attributes.age < 18);
             }   
             return result;
        },
        pollingInterval:0, 
        skip:false,
        refetchOnMountOrArgChange:false,
        refetchOnFocus:false,
        refetchOnReconnect:true,
    });



    const {data: stus, isSuccess, isLoading, refetch} = result; // 调用Api中的钩子查询数据

    return (
        <div>
            <button onClick={() => refetch()}>刷新</button>
            {isLoading && <p>数据加载中...</p>}
            {isSuccess && <StudentList stus={stus}/>}
        </div>
    );
};

export default App;

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

const store = configureStore({
    reducer:{
        [studentApi.reducerPath]:studentApi.reducer
    },

    middleware:getDefaultMiddleware =>
        getDefaultMiddleware().concat(studentApi.middleware)
});

setupListeners(store.dispatch); // 设置以后,将会支持 refetchOnFocus refetchOnReconnect

export default store;

RTKQ删除数据

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

const studentApi = createApi({
	reducerPath: 'studentApi',
	baseQuery: fetchBaseQuery({
		baseUrl: 'http://,,,/api/'
	}),
	endpoints(build) {
		return {
			getStudents: build.query({
				query() {
					// 用来请求子路径
					return 'students'
				},
				transformResponse(baseQueryReturnValue, meta, arg){
					return baseQueryReturnValue.data
				}
			}),
			getStudentById:build.query({
				query(id) {
					return `students/${id}`
				},
				transformResponse(baseQueryReturnValue, meta, arg){
					return baseQueryReturnValue,data
				},
				keepUnuseDataFor:60,
			}),
			delStudent: build.mutation({
				return {
					// 如果发送的get请求,需要返回一个对象来设置请求的信息
					url: `students/${id}`,
					method: 'delete'
				}
			})
		}
	}
})

export const {
	useGetStudentsQuery,
	useGetStudentByIdQuery,
	useDelStudentMutation,
} = studentApi

export default studentApi

RTKQ添加与修改

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

const studentApi = createApi({
	reducerPath: 'studentApi',
	baseQuery: fetchBaseQuery({
		baseUrl: 'http://,,,/api/'
	}),
	endpoints(build) {
		return {
			getStudents: build.query({
				query() {
					// 用来请求子路径
					return 'students'
				},
				transformResponse(baseQueryReturnValue, meta, arg){
					return baseQueryReturnValue.data
				}
			}),
			getStudentById:build.query({
				query(id) {
					return `students/${id}`
				},
				transformResponse(baseQueryReturnValue, meta, arg){
					return baseQueryReturnValue,data
				},
				keepUnuseDataFor:60,
			}),
			delStudent: build.mutation({
				return {
					// 如果发送的get请求,需要返回一个对象来设置请求的信息
					url: `students/${id}`,
					method: 'delete'
				}
			}),
			addStudent: build.mutation({
				query(stu) {
					return {
						url: 'students',
						method: 'post',
						body: {data: stu}
					}
				}
			}),
			updateStudent: build.mutation({
				query(stu) {
					return  {
						url: `students/${stu.id}`,
						method: 'put',
						body: {data: stu.attributes}
					}
				}
			})
		}
	}
})

export const {
    useGetStudentsQuery,
    useGetStudentByIdQuery,
    useDelStudentMutation,
    useAddStudentMutation,
    useUpdateStudentMutation
} = studentApi

export default studentApi

对于删除 增加 修改
调用钩子函数后 返回的是一个数组 数组中有两个东西 第一个是操作的触发器 第二个是结构集

const [delStudent, {isSuccess}] = useDelStudentMutation();
const [addStudent, {isSuccess:isAddSuccess}] = useAddStudentMutation();
const [updateStudent, {isSuccess:isUpdateSuccess}] = useUpdateStudentMutation();

RTKQ中,给Api切片请求的数据打上标签

给Api切片的配置对象添加 tagTypes属性,该属性的属性值是个数组。数组内存入需要用到的标签

    tagTypes: ['student'], // 用来指定Api中的标签类型

给Api切片的钩子函数添加 providesTags属性,属性值为标签数组。数组添加标签后,该钩子函数请求的数据就打上了标签

            getStudents: build.query({
                query() {
                    return 'students';
                },
                transformResponse(baseQueryReturnValue, meta, arg) {
                    return baseQueryReturnValue.data;
                },

                providesTags: [{type: 'student', id: 'LIST'}]
            }),

当providesTags属性的值为回调函数时,可以对标签的生效范围做更细致的划分
  参数1:网络请求的返回结果
  参数2:错误信息
  参数3:钩子函数中传入的实参
  //返回值为一个数组,符合数组元素条件的数据将生效。

            getStudentById: build.query({
                query(id) {
                    return `students/${id}`;
                },
                transformResponse(baseQueryReturnValue, meta, arg) {
                    return baseQueryReturnValue.data;
                },
                keepUnusedDataFor: 60,
                providesTags: (result, error, id) => [{type: 'student', id}]
            }),

给Api切片的钩子函数添加 invalidatesTags属性,属性值为标签输入。数组内存入标签后,对应的标签失效。标签失效后,网络请求的数据重新加载。
  - 注意:代码 invalidatesTags:[‘student’] ,是使得该标签的所有数据都失效

            updateStudent: build.mutation({
                query(stu) {
                    return {
                        url: `students/${stu.id}`,
                        method: 'put',
                        body: {data: stu.attributes}
                    };
                },
                invalidatesTags: ['student']
            }),

同样,invalidatesTags的属性值也可以是一个回调函数,该回调函数的结构与proviedsTags的回调函数结构一致

            updateStudent: build.mutation({
                query(stu) {
                    return {
                        url: `students/${stu.id}`,
                        method: 'put',
                        body: {data: stu.attributes}
                    };
                },
                invalidatesTags: ((result, error, stu) =>
                    [{type: 'student', id: stu.id}, {type: 'student', id: 'LIST'}])
            }),
        };

当不需要流动的参数时,invalidatesTags的属性值可直接写成对象的数组:

            addStudent: build.mutation({
                query(stu) {
                    return {
                        url: 'students',
                        method: 'post',
                        body: {data: stu}
                    };
                },
                invalidatesTags: [{type: 'student', id: 'LIST'}]
            }),

完整代码

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

const studentApi = createApi({
    reducerPath: 'studentApi', 
    baseQuery: fetchBaseQuery({
        baseUrl: "http://localhost:1337/api/"
    }),
    tagTypes: ['student'],
    endpoints(build) {
        return {
            getStudents: build.query({
                query() {
                    return 'students';
                },
                transformResponse(baseQueryReturnValue, meta, arg) {
                    return baseQueryReturnValue.data;
                },

                providesTags: [{type: 'student', id: 'LIST'}]
            }),
            getStudentById: build.query({
                query(id) {
                    return `students/${id}`;
                },
                transformResponse(baseQueryReturnValue, meta, arg) {
                    return baseQueryReturnValue.data;
                },
                keepUnusedDataFor: 60, 
                providesTags: (result, error, id) => [{type: 'student', id}]
            }),
            delStudent: build.mutation({
                query(id) {
                    return {
                        url: `students/${id}`,
                        method: 'delete'
                    };
                }
            }),
            addStudent: build.mutation({
                query(stu) {
                    return {
                        url: 'students',
                        method: 'post',
                        body: {data: stu}
                    };
                },
                invalidatesTags: [{type: 'student', id: 'LIST'}]
            }),
            updateStudent: build.mutation({
                query(stu) {
                    return {
                        url: `students/${stu.id}`,
                        method: 'put',
                        body: {data: stu.attributes}
                    };
                },
                invalidatesTags: ((result, error, stu) =>
                    [{type: 'student', id: stu.id}, {type: 'student', id: 'LIST'}])
            }),

        };
    }
});

export const {
    useGetStudentsQuery,
    useGetStudentByIdQuery,
    useDelStudentMutation,
    useAddStudentMutation,
    useUpdateStudentMutation
} = studentApi;

export default studentApi;

;