RTK Query 快速入门
- 如何设置和使用 Redux Toolkit 的“RTK Query”数据获取功能
- 了解 Redux 术语和概念
介绍
欢迎来到 Redux Toolkit Query 教程!本教程将简要介绍您 Redux Toolkit 的“RTK Query”数据获取功能,并教您如何正确开始使用它。
有关 RTK Query 的更深入教程,请参阅 Redux 核心文档网站上的完整 "Redux Essentials" 教程。
RTK Query 是一款高级数据获取和缓存工具,旨在简化 Web 应用程序中加载数据的常见情况。RTK Query 本身构建在 Redux Toolkit 核心之上,并利用 RTK 的 API(如 createSlice
和 createAsyncThunk
)来实现其功能。
RTK Query 包含在 @reduxjs/toolkit
包中,作为附加组件。使用 Redux Toolkit 时,您不需要使用 RTK Query API,但我们认为许多用户将在其应用程序中受益于 RTK Query 的数据获取和缓存功能。
如何阅读本教程
在本教程中,我们假设您正在将 Redux Toolkit 与 React 一起使用,但您也可以将其与其他 UI 层一起使用。这些示例基于 典型的 Create-React-App 文件夹结构,其中所有应用程序代码都在 src
中,但这些模式可以适应您使用的任何项目或文件夹设置。
设置您的商店和 API 服务
要了解 RTK Query 的工作原理,让我们逐步了解一个基本的使用示例。对于此示例,我们将假设您正在使用 React 并希望利用 RTK Query 自动生成的 React 钩子。
创建 API 服务
首先,我们将创建一个查询公开可用的 PokeAPI 的服务定义。
- TypeScript
- JavaScript
// Need to use the React-specific entry point to import createApi
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Pokemon } from './types'
// Define a service using a base URL and expected endpoints
export const pokemonApi = createApi({
reducerPath: 'pokemonApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
endpoints: (builder) => ({
getPokemonByName: builder.query<Pokemon, string>({
query: (name) => `pokemon/${name}`,
}),
}),
})
// Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints
export const { useGetPokemonByNameQuery } = pokemonApi
// Need to use the React-specific entry point to import createApi
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
// Define a service using a base URL and expected endpoints
export const pokemonApi = createApi({
reducerPath: 'pokemonApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
endpoints: (builder) => ({
getPokemonByName: builder.query({
query: (name) => `pokemon/${name}`,
}),
}),
})
// Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints
export const { useGetPokemonByNameQuery } = pokemonApi
使用 RTK Query,您通常在一个地方定义整个 API 定义。这很可能与您在其他库(如 swr
或 react-query
)中看到的不同,并且有几个原因。我们的观点是,与在整个应用程序的不同文件中拥有 X 个自定义钩子相比,当请求、缓存失效和一般应用程序配置都位于一个中心位置时,跟踪它们的行为要容易得多。
将服务添加到您的商店
RTKQ 服务会生成一个“切片 reducer”,该 reducer 应该包含在 Redux 根 reducer 中,以及一个处理数据获取的自定义中间件。两者都需要添加到 Redux 商店中。
- TypeScript
- JavaScript
import { configureStore } from '@reduxjs/toolkit'
// Or from '@reduxjs/toolkit/query/react'
import { setupListeners } from '@reduxjs/toolkit/query'
import { pokemonApi } from './services/pokemon'
export const store = configureStore({
reducer: {
// Add the generated reducer as a specific top-level slice
[pokemonApi.reducerPath]: pokemonApi.reducer,
},
// Adding the api middleware enables caching, invalidation, polling,
// and other useful features of `rtk-query`.
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(pokemonApi.middleware),
})
// optional, but required for refetchOnFocus/refetchOnReconnect behaviors
// see `setupListeners` docs - takes an optional callback as the 2nd arg for customization
setupListeners(store.dispatch)
import { configureStore } from '@reduxjs/toolkit'
// Or from '@reduxjs/toolkit/query/react'
import { setupListeners } from '@reduxjs/toolkit/query'
import { pokemonApi } from './services/pokemon'
export const store = configureStore({
reducer: {
// Add the generated reducer as a specific top-level slice
[pokemonApi.reducerPath]: pokemonApi.reducer,
},
// Adding the api middleware enables caching, invalidation, polling,
// and other useful features of `rtk-query`.
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(pokemonApi.middleware),
})
// optional, but required for refetchOnFocus/refetchOnReconnect behaviors
// see `setupListeners` docs - takes an optional callback as the 2nd arg for customization
setupListeners(store.dispatch)
使用Provider
包装您的应用程序
如果您还没有这样做,请遵循将 Redux 商店提供给您的 React 应用程序组件树其余部分的标准模式。
- TypeScript
- JavaScript
import * as React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import App from './App'
import { store } from './store'
const rootElement = document.getElementById('root')
render(
<Provider store={store}>
<App />
</Provider>,
rootElement
)
import * as React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import App from './App'
import { store } from './store'
const rootElement = document.getElementById('root')
render(
<Provider store={store}>
<App />
</Provider>,
rootElement
)
在组件中使用查询
定义服务后,您可以导入钩子来发出请求。
- TypeScript
- JavaScript
import * as React from 'react'
import { useGetPokemonByNameQuery } from './services/pokemon'
export default function App() {
// Using a query hook automatically fetches data and returns query values
const { data, error, isLoading } = useGetPokemonByNameQuery('bulbasaur')
// Individual hooks are also accessible under the generated endpoints:
// const { data, error, isLoading } = pokemonApi.endpoints.getPokemonByName.useQuery('bulbasaur')
return (
<div className="App">
{error ? (
<>Oh no, there was an error</>
) : isLoading ? (
<>Loading...</>
) : data ? (
<>
<h3>{data.species.name}</h3>
<img src={data.sprites.front_shiny} alt={data.species.name} />
</>
) : null}
</div>
)
}
import * as React from 'react'
import { useGetPokemonByNameQuery } from './services/pokemon'
export default function App() {
// Using a query hook automatically fetches data and returns query values
const { data, error, isLoading } = useGetPokemonByNameQuery('bulbasaur')
// Individual hooks are also accessible under the generated endpoints:
// const { data, error, isLoading } = pokemonApi.endpoints.getPokemonByName.useQuery('bulbasaur')
return (
<div className="App">
{error ? (
<>Oh no, there was an error</>
) : isLoading ? (
<>Loading...</>
) : data ? (
<>
<h3>{data.species.name}</h3>
<img src={data.sprites.front_shiny} alt={data.species.name} />
</>
) : null}
</div>
)
}
发出请求时,您可以通过多种方式跟踪状态。您始终可以检查data
、status
和error
以确定要渲染的正确 UI。此外,useQuery
还为最新请求提供了isLoading
、isFetching
、isSuccess
和isError
等实用程序布尔值。
基本示例
好的,这很有趣……但是如果您想同时显示多个神奇宝贝呢?如果多个组件加载同一个神奇宝贝会发生什么?
高级示例
RTK Query 确保订阅相同查询的任何组件始终使用相同的数据。RTK Query 自动对请求进行去重,因此您无需担心检查正在进行的请求和您端的性能优化。让我们评估下面的沙盒 - 确保检查浏览器开发者工具中的网络面板。您将看到 3 个请求,即使有 4 个订阅的组件 - bulbasaur
只发出一个请求,并且加载状态在两个组件之间同步。为了好玩,尝试将下拉菜单的值从 Off
更改为 1s
,以查看当重新运行查询时此行为是否继续。