鉴于我找不到一个合适的引子来引出今天的focus,于是选择直奔主题。 本篇内容我主要介绍RTK + RTK Query模式管理基于React开发前端应用的数据管理理论与实践。
理论概念
RTK是什么?
Redux Toolkit, 简称RTK,redux的工具集,主要目的是react使用redux更加便捷,其底层逻辑依然是redux的概念,Toolkit提供了一些固定的模式,我们只需要根据其样板文件就可以将redux无缝集成到react的项目中,其目的就是让开发者focus项目的业务逻辑即可。
RTK Query是什么?为什么有RTK Query?
RTK Query是RTK的一个插件(addon),不需要单独安装,直接从RTK的包中就可以导出。用过react-query或者useRequest的会更好接受这个,这个RTK Query跟这两个事物还是蛮有重合的。说到这就不得不说下自己的一点小感受,不知道从什么时候开始,前端开发者开始把每个请求的状态都抽象出来了,当我还沉浸在axios如此好用的喜悦中之后,猛然发现社区里有useRequest这样的hook,然后又继而到react-query,RTK Query这些事物。嗯,就是这样,觉得不学就落后了应该学但是实际好像不用这些也可以写项目,这种纠结蛮难受,最终半推半掩的接受这些新事物,虽然现在已不算什么新事物了。 最终选择RTK Query,纯粹是因为代码洁癖,因为不想引入更多的东西,想用一套链路完事,所以RTK Query平替了axios的使用。
总的来说,RTK Query承接了API层的数据问题,加上RTK提供的Redux的前端数据管理方式,基本实现了一个完整的数据链路,是一个完备的数据管理方案。
实践操作
这部分主要介绍一下,react项目中使用RTK+RTK Query管理数据的具体实施步骤:
1、依赖安装
npm i redux react-redux @reduxjs/toolkit
2、创建数据仓库
import {configureStore} from '@reduxjs/toolkit'
// 导出数据仓库store
export const store = configureStore({
reducer: {},
})
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch
3、将store集成到react application中
// 导入步骤二创建的store
import {store} from './store.ts
import {Provider} from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
4、根据slice样板文件创建带有业务数据的state slice
// 这个文件来自官网(https://redux-toolkit.js.org/tutorials/quick-start)提供的一个样板文件
import { createSlice } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'
const initialState: CounterState = {
value: 0,
}
export const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state) => {
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload
},
},
})
// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
5、将步骤4中的slice集成到store中
import {configureStore} from '@reduxjs/toolkit'
// 步骤4中导出的slice
import counterReducer from './counterSlice'
// 导出数据仓库store
export const store = configureStore({
reducer: {
counter: counterReducer // 将counterSlice放到这里
},
})
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch
6、RTK Query创建API service
// 直接从toolkit中导入即可
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
// Define a service using a base URL and expected endpoints
export const userApi = createApi({
reducerPath: 'userApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://domain:port/api/' }),
endpoints: (builder) => ({
getUserInfo: builder.query<any, string>({
query: (name) => `getUserInfo/${name}`,
}),
}),
})
// 导出自动生成的hooks使用
export const { useGetUserInfoQuery } = userApi
7、将API service也理解为一种state slice集成到store中
import {configureStore} from '@reduxjs/toolkit'
// 步骤4中导出的slice
import counterReducer from './counterSlice'
// 步骤6中导出的api slice
import {userApi} from './userApi'
// 导出数据仓库store
export const store = configureStore({
reducer: {
counter: counterReducer, // 将counterSlice放到这里
[userApi.reducerPath]: userApi.reducer
},
middleware: (getDefaultMiddleware) => {
getDefaultMiddleware().concat(userApi.middleware) // 可以当作样板代码
}
})
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch
8、react component中使用store中的数据和action
// 导入userApi中导出自动生成的query hook
import {useGetUserInfoQuery} from './userApi'
export default function App() {
// hook调用返回data,error,isLoading,因为是hook,数据变更后会引起组件重新执行,完成业务逻辑
const {data, error, isLoading} = useGetUserInfoQuery('name')
// 这里放业务逻辑展示即可
return (
<div></div>
)
}
小结
至此,一个完整的RTK+RTK Query管理前端数据的方案已经完毕。上述代码大部分截取其官网展示的示例code,只是将RTK和RTK Query的示例代码拼接在一起形成一个完整的实践操作。目前该套方案已经在多个项目中使用,基本覆盖了业务需求场景,总体感受这套方案还算清晰优雅。