你会学到什么
- 如何使用redux-toolkit管理项目的状态
- 如何使用rtkquery进行api接口的请求
- 如何使用axios替换rtkquery的默认fetch请求
- 如何使用rtkquery发送get请求和post请求
- 如何在组件mouted的时候,不立即执行rtkquery返回的hooks函数
什么是redux-toolkit
Redux Toolkit是一个官方推荐的用于简化Redux开发的工具集。它提供了一组工具和API,可以帮助开发者更快速、更简洁地编写Redux代码。Redux Toolkit包含了一些常用的Redux模块,如createSlice、createAsyncThunk和createReducer等,这些模块可以减少样板代码的编写,提高开发效率。此外,Redux Toolkit还集成了Immer库,使得在Redux中使用不可变数据更加方便。总之,Redux Toolkit旨在简化Redux的使用,提供更好的开发体验。
什么是rtkquery
Redux Toolkit Query (RTK Query) 是 Redux Toolkit 的一个插件,用于处理数据获取和管理。它提供了一种简化和标准化数据获取的方式,使得在 Redux 应用中进行数据请求变得更加容易和高效。
RTK Query 的主要特点包括:
- 自动化的数据获取和缓存管理:RTK Query 可以自动处理数据的获取、缓存和更新,无需手动编写大量的异步 action 和 reducer。
- 强大的数据查询和变换能力:RTK Query 提供了丰富的查询和变换操作,可以轻松地对数据进行过滤、排序、分页等操作。
- 内置的网络层和错误处理:RTK Query 内置了网络请求层,可以处理网络请求的发送和响应,同时也提供了错误处理和重试机制。
- 集成了 Redux Toolkit:RTK Query 是 Redux Toolkit 的一部分,可以与 Redux 的其他功能无缝集成,如状态管理、中间件等。
总之,RTK Query 是一个强大且易于使用的工具,可以大大简化 Redux 应用中的数据获取和管理过程,提高开发效率和代码质量。
如何在RTK Query里边使用axios
RTK Query默认是使用的fetch作为请求的,但是我们也可以使用我们自己封装的axios作为默认请求方法。
比如首先在项目的utils中新建axios.ts,对于axios进行简单的二次封装
js
import axios from 'axios';
import { useNavigate } from 'react-router-dom';
// 用于显示错误信息
const request = axios.create({
baseURL: import.meta.env.VITE_REACT_APP_API_BASE_URL, // api的base_url
timeout: 5000, // 请求超时时间
});
// 请求拦截器
request.interceptors.request.use(
(config) => {
// 在这里可以做一些请求前的操作
const myToken = localStorage.getItem('token');
if (myToken) {
config.headers['Token'] = 'Bearer ' + myToken;
}
return config;
},
(error) => {
// 请求错误处理
// for debug
Promise.reject(error);
}
);
// 响应拦截器
request.interceptors.response.use(
(res) => {
return res.data;
},
(error) => {
if (error?.response?.status === 401) {
const navigator = useNavigate();
navigator('/login');
}
// console.log(999, error.message);
// message.error(error.message);
return Promise.reject(error);
}
);
export default request;
然后在项目新建文件加services,在services下面新建api.ts,在api.ts里边新建axiosBaseQuery函数,使用axiosBaseQuery作为rtkQuery的默认请求方法。 代码如下:
js
import { createApi } from '@reduxjs/toolkit/query/react';
import type { BaseQueryFn } from '@reduxjs/toolkit/query/react';
import type { AxiosRequestConfig, AxiosError } from 'axios';
import request from '@/utils/axios';
// 定义自己的axios请求函数,代替rtkquery提供的fetch
const axiosBaseQuery =
(
{ baseUrl }: { baseUrl: string } = { baseUrl: '' }
): BaseQueryFn<
{
url: string;
method: AxiosRequestConfig['method'];
data?: AxiosRequestConfig['data'];
params?: AxiosRequestConfig['params'];
},
unknown,
unknown
> =>
async ({ url, method, data, params }) => {
try {
// Axios.defaults.baseURL = 'https://jsonplaceholder.typicode.com/';
const result = await request({
url: baseUrl + url,
method,
data,
params,
});
// 必须return object,否则rtkquery不认识
return { data: result.data };
} catch (axiosError) {
const err = axiosError as AxiosError;
return {
error: {
status: err.response?.status,
data: err.response?.data || err.message,
},
};
}
};
const apiService = createApi({
reducerPath: 'api',
baseQuery: axiosBaseQuery(),
endpoints: () => ({}),
});
export default apiService;
然后把函数注册到store.ts里边
js
import { configureStore } from '@reduxjs/toolkit';
import apiService from '@/services/api';
import searchReducer from '@/features/search';
export const store = configureStore({
reducer: {
searchSlice: searchReducer,
// 引入文件进行注册
[apiService.reducerPath]: apiService.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(apiService.middleware),
});
// 根节点的rootState类型
export type RootState = ReturnType<typeof store.getState>;
// 根节点的AppDispatch类型
export type AppDispatch = typeof store.dispatch;
然后在react 应用的顶层挂载上去
js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { RouterProvider } from 'react-router-dom';
import 'antd/dist/reset.css';
import './app.css';
import './iconfont.js';
import routeres from './routes/index';
import { store } from './store/store';
import { Provider } from 'react-redux';
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<Provider store={store}>
<RouterProvider router={routeres}></RouterProvider>
</Provider>
</React.StrictMode>
);
当做完上面这些以后,基本的前置操作都已经完成了,下面定义一些状态
js
import apiService from './api';
export interface Post {
id: number;
userId: number;
title: string;
body: string;
}
export interface CreatePostDto {
title: string;
body: string;
}
export const postService = apiService.injectEndpoints({
endpoints: (builder) => ({
// 请求文件
getPosts: builder.query<Post[], null>({
query: () => ({ method: 'GET', url: '/api/posts' }),
}),
// 修改文件
createPost: builder.mutation<Post, CreatePostDto>({
query: (data) => ({
method: 'POST',
url: '/api/posts1',
// We pass static userId to simplify this part
data: { userId: 1, ...data },
}),
}),
}),
});
// 自动生成自定义hooks, useGetPostQuery 是去获取,在组件挂载的时候,立即执行,
// useCreatePostMutation 会返回执行的函数,手动触发
// useLazyGetPostsQuery 是不在组件挂载的时候执行,需要手动执行
export const { useGetPostsQuery, useCreatePostMutation, useLazyGetPostsQuery } =
postService;
然后就可以在组件进行使用了
比如如下方式:
js
const {
data: posts,
isLoading,
error,
refetch,
} = useGetPokemonByNameQuery('ssd');
console.log(useAppSelector((state) => state));
// const [createPosts, { isLoading }] = useGetPokemonByNameQuery();
// useEffect(() => {
// createPosts({
// title: '11',
// body: '1223',
// });
// }, []);
通过查看浏览器,可以看到现在已经可以使用了
如何在组件mounted的时候,不立即触发函数
这里有两种方法
- 一种是使用一种状态进行Conditional Fetching
js
import * as React from 'react'
import { useGetPokemonByNameQuery } from './services/pokemon'
import type { PokemonName } from './pokemon.data'
export const Pokemon = ({ name }: { name: PokemonName }) => {
const [skip, setSkip] = React.useState(true)
const { data, error, isLoading, isUninitialized } = useGetPokemonByNameQuery(
name,
{
skip,
}
)
const SkipToggle = () => (
<button onClick={() => setSkip((prev) => !prev)}>
Toggle Skip ({String(skip)})
</button>
)
return (
<>
{error ? (
<>Oh no, there was an error</>
) : isUninitialized ? (
<div>
{name} - Currently skipped - <SkipToggle />
</div>
) : isLoading ? (
<>loading...</>
) : data ? (
<>
<div>
<h3>{data.species.name}</h3>
<img src={data.sprites.front_shiny} alt={data.species.name} />
</div>
<SkipToggle />
</>
) : null}
</>
)
}
这里最关键的就是skip,通过控制skip的状态来控制是否执行
- 使用上面提到的useLazyGetPostsQuery方式, useLazyGetPostsQuery方式会返回一个控制函数,当你需要的时候就可以调用了,这样就不会在组件刚开始挂载的时候进行执行了。