使用 React Query 管理状态。〽️
React Query是一个大型且完整的库,它简化了客户端向服务器发出请求的工作,甚至执行更多操作。
但是你知道吗?这个库可以用作状态管理器。它可能是 redux-toolkit、zustand 等工具的替代方案。在本文中,我将向你展示如何通过这种方式实现它。
🚨 注意:要理解本文,您应该具备有关如何使用 React Query 的基本知识以及一些 TypeScript 的基本知识。
目录。
📌需要用到的技术。📌 创建项目。📌
第一步。📌 创建页面。📌 配置React Query。📌使用 React Query 作为状态管理器。📌创建函数来 发起请求。📌使用 React Query 获取数据。📌
向状态添加新数据。📌 从状态中移除数据。📌 更新状态数据。📌结论。
📢 要使用的技术。
- React JS 18.2.0
- React Query 4.20.4
- React Router Dom 6.6.1
- TypeScript 4.9.3
- Vite JS 4.0.0
- CSS vanilla(您可以在本文末尾的存储库中找到样式)
📢 创建项目。
我们将为该项目命名:(state-management-rq
可选,您可以随意命名)。
npm create vite@latest
我们使用 Vite JS 创建项目,并选择 React with TypeScript。
然后运行以下命令导航到刚刚创建的目录。
cd state-management-rq
然后我们安装依赖项。
npm install
然后我们在代码编辑器中打开项目(在我的情况下是 VS 代码)。
code .
📢 第一步。
首先,我们将安装一个 dom 路由器,以便能够在我们的应用程序中创建几个页面。
npm install react-router-dom
因此,让我们创建一个src/layout文件夹,用于创建一个非常简单的导航菜单,该菜单将出现在所有页面上。在src/layout
中,我们创建index.tsx文件并添加以下内容:
import { NavLink, Outlet } from 'react-router-dom'
type LinkActive = { isActive: boolean }
const isActiveLink = ({ isActive }: LinkActive) => `link ${isActive ? 'active' : ''}`
export const Layout = () => {
return (
<>
<nav>
<NavLink className={isActiveLink} to="/">Home 🏠</NavLink>
<NavLink className={isActiveLink} to="/create">Create ✍️</NavLink>
</nav>
<hr className='divider' />
<div className='container'>
<Outlet />
</div>
</>
)
}
然后在src/App.tsx文件中,我们将删除所有内容。然后创建基本路由。
注意:我们将使用createBrowserRouter设置路由,但如果您愿意,您可以使用 react-router-dom 仍然具有的组件(如、、
<BrowserRouter/>
等)代替createBrowserRouter。<Routes/>
<Route/>
通过createBrowserRouter ,我们将创建一个对象来添加路由。请注意,我只有一个父路由,我展示的是导航菜单,而这个路由有 3 个子路由,目前它们尚未创建页面。
最后我们创建默认导出的组件 App,该组件将渲染一个 react-router-dom 组件,该组件<RouterProvider/>
接收我们刚刚创建的路由器。
通过它我们可以在不同的路线之间导航。
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { Layout } from './layout';
import { Home } from './pages/home';
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{
index: true,
element: <>create</>,
},
{
path: "/create",
element: <>create</>,
},
{
path: "/:id",
element: <>edit</>,
},
]
}
]);
const App = () => ( <RouterProvider router={router} /> )
export default App
然后我们将回到这个文件添加更多内容👀。
📢 创建页面。
现在,我们将为之前定义的路径创建三个页面。
创建一个新文件夹src/pages,并在其中创建 3 个文件。
- 主页.tsx
在此文件中,我们仅列出来自 API 的数据,因此目前我们仅放置以下内容:
import { Link } from 'react-router-dom'
export const Home = () => {
return (
<>
<h1>Home</h1>
<div className="grid">
<Link to={`/1`} className='user'>
<span>username</span>
</Link>
</div>
</>
)
}
- 创建用户.tsx。
此页面仅用于创建新用户或新数据。因此,我们将创建一个表单。在这种情况下,我不会使用状态来控制表单的输入,而是简单地使用在表单执行 onSubmit 时触发的事件(务必将属性名称添加到输入中)。
export const CreateUser = () => {
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
const form = e.target as HTMLFormElement
const data = Object.fromEntries(new FormData(form))
// TODO: create new user
form.reset()
}
return (
<div>
<h1>Create User</h1>
<form onSubmit={handleSubmit} className='mt'>
<input name='user' type="text" placeholder='Add new user' />
<button>Add User</button>
</form>
</div>
)
}
- editUser.tsx
在此页面中将编辑选定的用户,我们将通过 URL 的参数获取他的 ID,就像我们在创建路由器时建立的那样。
import { useParams } from 'react-router-dom';
export const EditUser = () => {
const params = useParams()
const { id } = params
if (!id) return null
return (
<>
<span>Edit user {id}</span>
</>
)
}
现在我们需要将这些页面放置在路由器中!
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { Layout } from './layout';
import { CreateUser } from './pages/createUser';
import { EditUser } from './pages/editUser';
import { Home } from './pages/home';
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{
index: true,
element: <Home />,
},
{
path: "/create",
element: <CreateUser />,
},
{
path: "/:id",
element: <EditUser />,
},
]
}
]);
const App = () => (
<RouterProvider router={router} />
)
export default App
📢 配置 React Query。
首先我们将安装该库。
npm install @tanstack/react-query
然后我们在src/App.tsx文件中配置提供程序。
- 首先我们将创建queryClient。
在这种情况下,我们将保留这些选项,这将帮助我们将 React Query 用作状态管理器:
refetchOnWindowFocus
:当您退出应用程序然后返回时,React Query 会返回以发出数据请求。refetchOnMount
:当组件重新安装时,它将再次发出请求。retry
:重试请求的次数。
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
refetchOnMount: false,
retry: 1,
},
},
});
- 然后我们需要导入为我们提供 React Query 的提供程序并向其发送我们刚刚创建的 queryClient。
const App = () => (
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
</QueryClientProvider>
)
- 最后,虽然它是可选的,但它非常有用,我们将安装 React Query devtools,这将有很大帮助。
npm install @tanstack/react-query-devtools
现在我们将 devtools 放在 React Query 提供程序中。
- 该文件最终看起来是这样的。
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { Layout } from './layout';
import { CreateUser } from './pages/createUser';
import { EditUser } from './pages/editUser';
import { Home } from './pages/home';
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{
index: true,
element: <Home />,
},
{
path: "/create",
element: <CreateUser />,
},
{
path: "/:id",
element: <EditUser />,
},
]
}
]);
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnMount: false,
refetchOnWindowFocus: false,
retry: 1
},
},
});
const App = () => (
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools initialIsOpen={false} />
<RouterProvider router={router} />
</QueryClientProvider>
)
export default App
📢 使用 React Query 作为状态管理器。
首先,我们将创建要执行的queryFn 。
📢 创建函数来发出请求。
我们将创建一个文件夹src/api,并创建文件user.ts,其中包含向 API 发出请求的函数。
为了节省创建 API 的时间,我们将使用JSON 占位符,因为它允许我们进行“CRUD”操作,而不仅仅是 GET 请求。
**我们将创建 4 个函数来执行 CRUD。
首先我们设置常量和接口
界面如下:
export interface User {
id: number;
name: string;
}
常数为:
import { User } from '../interface';
const URL_BASE = 'https://jsonplaceholder.typicode.com/users'
const headers = { 'Content-type': 'application/json' }
- 首先,我们将创建一个函数来请求用户。该函数必须返回一个承诺。
export const getUsers = async (): Promise<User[]> => {
return await (await fetch(URL_BASE)).json()
}
- 然后该函数创建一个新用户,该函数接收一个用户并返回一个解决新用户的承诺。
export const createUser = async (user: Omit<User, 'id'>): Promise<User> => {
const body = JSON.stringify(user)
const method = 'POST'
return await (await fetch(URL_BASE, { body, method, headers })).json()
}
- 另一个用于编辑用户的函数,它接收要编辑的用户并返回解析已编辑用户的承诺。
export const editUser = async (user: User): Promise<User> => {
const body = JSON.stringify(user)
const method = 'PUT'
return await (await fetch(`${URL_BASE}/${user.id}`, { body, method, headers })).json()
}
- 最后,一个用于删除用户的函数,它接收一个 id。由于从 API 中删除记录时不会返回任何内容,因此我们将返回一个 Promise,该 Promise 解析该id来识别被删除的用户。
export const deleteUser = async (id: number): Promise<number> => {
const method = 'DELETE'
await fetch(`${URL_BASE}/${id}`, { method })
return id
}
该文件看起来是这样的:
import { User } from '../interface';
const URL_BASE = 'https://jsonplaceholder.typicode.com/users'
const headers = { 'Content-type': 'application/json' }
export const getUsers = async (): Promise<User[]> => {
return await (await fetch(URL_BASE)).json()
}
export const createUser = async (user: Omit<User, 'id'>): Promise<User> => {
const body = JSON.stringify(user)
const method = 'POST'
return await (await fetch(URL_BASE, { body, method, headers })).json()
}
export const editUser = async (user: User): Promise<User> => {
const body = JSON.stringify(user)
const method = 'PUT'
return await (await fetch(`${URL_BASE}/${user.id}`, { body, method, headers })).json()
}
export const deleteUser = async (id: number): Promise<number> => {
const method = 'DELETE'
await fetch(`${URL_BASE}/${id}`, { method })
return id
}
📢 使用 React Query 获取数据。
我们不会将 React Query 代码直接放在组件中,而是将它们一次性放在自定义钩子中,以便将代码集中在一个地方。
因此我们将创建一个文件夹src/hook并在其中创建一个名为useUser.tsx的文件。
我们将创建的第一个自定义钩子是useGetUsers ,它只返回useQuery钩子返回的属性。
请注意, useQuery需要 2 个参数,一个字符串数组来标识查询,第二个参数是我们之前完成的函数,即从 API 获取用户。
import { useQuery } from '@tanstack/react-query';
import { getUsers } from '../api/user';
const key = 'users'
export const useGetUsers = () => {
return useQuery([key], getUsers);
}
啊哈拉,我们使用GetUsers。但是,如果我们使用了 useQuery,则需要建立queryKey和queryFn,这样才能轻松阅读
import { Link } from 'react-router-dom'
import { useGetUsers } from '../hook/useUser'
export const Home = () => {
const { data, isLoading, isError } = useGetUsers()
return (
<>
<h1>Home</h1>
{isLoading && <span>fetching a character...</span>}
{isError && <span>Ups! it was an error 🚨</span>}
<div className="grid">
{
data?.map(user => (
<Link to={`/${user.id}`} key={user.id} className='user'>
<span>{user.name}</span>
</Link>
))
}
</div>
</>
)
}
到目前为止,我们仅设置了数据并将该数据存储在缓存中(它将充当存储状态的存储库),我们尚未在其他地方使用/修改该组件的状态。
📢 向我们的状态添加新数据。
让我们转到src/hooks/useUser.tsx并创建一个新的自定义钩子来创建新用户。
export const useCreateUser = () => {}
在这种情况下以及接下来的情况下,我们将使用useMutation,因为我们将执行 POST 请求来创建新记录。
useMutation 接收要执行的 queryFn,在这种情况下,我们将传递我们创建的用于添加新用户的函数。
export const useCreateUser = () => {
return useMutation(createUser)
}
我们将传递第二个参数,它是一个对象,它将访问onSuccess属性,该属性是在请求成功时执行的函数。
onSuccess 接收几个参数,我们将使用第一个参数,即createUser函数返回的数据,在本例中必须是新用户。
export const useCreateUser = () => {
return useMutation(createUser, {
onSuccess: (user: User) => {}
})
}
现在我们要做的是访问缓存(我们的状态)并添加这个新创建的用户。
对于此任务,我们将使用另一个 React Query 钩子useQueryClient。
🚨 注意:不要解构钩子 useQueryClient 的任何属性,因为您将丢失引用,并且该属性将无法按您希望的方式工作。
现在,在onSuccess函数体中,让我们使用setQueryData属性设置新数据。
setQueryData,需要2个参数,第一个是queryKey,用于标识要从缓存的哪个部分获取数据并进行修改。
export const useCreateUser = () => {
const queryClient = useQueryClient();
return useMutation(createUser, {
onSuccess: (user: User) => {
queryClient.setQueryData([key])
}
})
}
第二个参数是设置新数据的函数。该函数必须接收缓存中已有的数据,在本例中可以是用户数组,也可以是未定义的数据。
将要进行的将是验证,如果缓存中已经有用户,我们只添加新用户并传播以前的用户,否则我们只返回在数组中创建的用户。
export const useCreateUser = () => {
const queryClient = useQueryClient();
return useMutation(createUser, {
onSuccess: (user: User) => {
queryClient.setQueryData([key],
(prevUsers: User[] | undefined) => prevUsers ? [user, ...prevUsers] : [user]
)
// queryClient.invalidateQueries([key])
}
})
}
🚨 注意:观察前面代码中注释的行
queryClient.invalidateQueries([key])
这行代码用于使缓存无效并重新从服务器请求数据。这通常是你在发出 POST、PUT、DELETE 等请求时需要执行的操作。
就我而言,我注释掉了这一行,因为 JSON 占位符 API 不会修改数据,它只是模拟修改数据。所以,如果我发出 DELETE 请求删除一条记录,一切正常,然后我输入invalidateQueries,它会再次返回所有用户,看起来数据没有任何变化。
一旦明确了这一点,我们将使用src/pages/createUser.tsx中的自定义钩子。
我们设置了自定义钩子,在这种情况下,您可以解构此钩子返回的道具,但我不会只是为了好玩而这样做(尽管当我们使用多个钩子时,这种语法将是一个很好的选择,以避免与道具的名称冲突)。
const create_user = useCreateUser()
现在在 handleSubmit 中,我们将访问 mutateAsync 属性,并且由于 TypeScript,我们知道必须传递哪些参数,即新用户的名称。
await create_user.mutateAsync({ name: data.user as string })
如果你想知道我们从哪里得到这个参数,它来自函数,它来自文件src/>api/user.ts的 createUser 函数,它取决于它作为参数接收的内容,它是我们将作为参数发送的内容。
页面看起来会像这样:
import { useCreateUser } from '../hook/useUser'
export const CreateUser = () => {
const create_user = useCreateUser()
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
const form = e.target as HTMLFormElement
const data = Object.fromEntries(new FormData(form))
await create_user.mutateAsync({ name: data.user as string })
form.reset()
}
return (
<div>
<h1>Create User</h1>
<form onSubmit={handleSubmit} className='mt'>
<input name='user' type="text" placeholder='Add new user' />
{create_user.isLoading && <span>creating user...</span>}
<button>Add User</button>
{create_user.isSuccess && <span>User created successfully ✅</span>}
{create_user.isError && <span>Ups! it was an error 🚨</span>}
</form>
</div>
)
}
📢 从状态中删除数据。
现在是时候删除数据了,步骤与我们创建数据时类似。
- 我们创建自定义钩子useDeleteUser。
- 我们使用useMutation,发送要执行的函数deleteUser。
- 我们访问 onSuccess 属性来执行该函数。
- 一旦请求成功,我们就使用 useQueryClient 来修改缓存中的数据。
- 我们将 queryKey 发送到setQueryData属性和函数,我们验证是否有数据,如果有,我们根据从 onSuccess 收到的 ID 过滤数据并排除刚刚删除的用户,返回没有已删除用户的新数组。
export const useDeleteUser = () => {
const queryClient = useQueryClient();
return useMutation(deleteUser, {
onSuccess: (id) => {
queryClient.setQueryData([key],
(prevUsers: User[] | undefined) => prevUsers ? prevUsers.filter(user => user.id !== id) : prevUsers
// queryClient.invalidateQueries([key])
)
}
});
}
我们在src/pages/editUser.tsx中使用自定义钩子。
但在我们将要执行的操作分离到不同的组件之前,我们将在同一个文件中创建一个组件,命名为DeleteUser,它接收要删除的用户的 ID。
import { useParams } from 'react-router-dom';
import { useDeleteUser } from '../hook/useUser';
import { User } from '../interface';
export const EditUser = () => {
const params = useParams()
const { id } = params
if (!id) return null
return (
<>
<DeleteUser id={+id} />
</>
)
}
DeleteUser将具有以下内容。
我们设置自定义钩子useDeleteUser并访问 mutateAsync 方法来执行请求并向其发送 id。
export const DeleteUser = ({ id }: Pick<User, 'id'>) => {
const delete_user = useDeleteUser()
const onDelete = async () => {
await delete_user.mutateAsync(id)
}
return (
<>
{delete_user.isLoading && <span>deleting user...</span>}
<button onClick={onDelete}>Delete User</button>
{delete_user.isSuccess && <span>User deleted successfully ✅, go back home</span>}
{delete_user.isError && <span>Ups! it was an error 🚨</span>}
</>
)
}
就是这样,删除后返回主页,你会发现用户已被正确删除。当然,如果你刷新浏览器,这个用户会重新出现,因为我们使用了 JSON 占位符。
📢 更新状态数据。
现在是时候按照相同的步骤更新用户了。
- 我们创建自定义钩子useEditUser。
- 我们使用useMutation,发送要执行的函数editUser。
- 我们访问 onSuccess 属性来执行该函数。
- 一旦请求成功,我们就使用 useQueryClient 来修改缓存中的数据。
- 我们将 queryKey 发送到setQueryData属性和函数,我们验证数据是否存在,如果是,我们通过 ID 识别已修改的用户并分配已修改的新值。
export const useEditUser = () => {
const queryClient = useQueryClient();
return useMutation(editUser, {
onSuccess: (user_updated: User) => {
queryClient.setQueryData([key],
(prevUsers: User[] | undefined) => {
if (prevUsers) {
prevUsers.map(user => {
if (user.id === user_updated.id) {
user.name = user_updated.name
}
return user
})
}
return prevUsers
}
)
}
})
}
现在让我们转到src/pages/editUser.tsx并创建另外两个组件来向您展示一个缺点。
我们创建组件ViewUser来查看用户,以及创建 EditUser来作为编辑用户的表单。
import { useParams } from 'react-router-dom';
import { useDeleteUser, useEditUser, useGetUsers } from '../hook/useUser';
import { User } from '../interface';
export const EditUser = () => {
const params = useParams()
const { id } = params
if (!id) return null
return (
<>
<ViewUser id={+id} />
<EditUserForm id={+id} />
<DeleteUser id={+id} />
</>
)
}
ViewUser接收 id,并使用 useGetUsers 获取所有用户(这不会触发其他请求,但会访问缓存中的用户)。
我们筛选用户并将其显示在屏幕上。
export const ViewUser = ({ id }: Pick<User, 'id'>) => {
const get_users = useGetUsers()
const user_selected = get_users.data?.find(user => user.id === +id)
if (!user_selected) return null
return (
<>
<h1>Edit user: {id}</h1>
<span>User name: <b>{user_selected?.name}</b></span>
</>
)
}
EditUser ,它也接收一个 ID。实际上,这个组件与createUser.tsx页面中的组件非常相似,你甚至可以重用它,但在本例中我不会这么做。
我们使用自定义钩子useEditUser,访问其方法mutateAsync并传递必要的参数。这样您就可以编辑选定的用户了。
export const EditUserForm = ({ id }: Pick<User, 'id'>) => {
const edit_user = useEditUser()
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
const form = e.target as HTMLFormElement
const data = Object.fromEntries(new FormData(form))
await edit_user.mutateAsync({ name: data.user as string, id })
form.reset()
}
return (
<>
<form onSubmit={handleSubmit}>
<input name='user' type="text" placeholder='Update this user' />
{edit_user.isLoading && <span>updating user...</span>}
<button>Update User</button>
{edit_user.isSuccess && <span>User updated successfully ✅</span>}
{edit_user.isError && <span>Ups! it was an error 🚨</span>}
</form>
</>
)
}
但请注意,您会注意到,当正确更新用户时,ViewUser组件不会被渲染,也就是说,它保留了前一个用户的名称值。但是,如果您返回主页,您会注意到用户的名称已更新。
这是因为需要新的渲染来更改ViewUser组件。
对此我想到了一个解决方案。创建一个新的自定义钩子来处理可观察对象,并感知缓存中某个部分的变化。
在这个自定义钩子中,我们将使用另一个自定义钩子useGetUsers和钩子useQueryClient。
- 首先,我们使用 useGetUsers 并返回其 props,但我们覆盖了 prop 数据,因为我们必须注意它的变化。
export const useGetUsersObserver = () => {
const get_users = useGetUsers()
return {
...get_users,
data:[],
}
}
- 我们创建一个状态来管理用户数组,并将该状态分配给 prop 数据。
export const useGetUsersObserver = () => {
const get_users = useGetUsers()
const [users, setUsers] = useState<User[]>()
return {
...get_users,
data: users,
}
}
- 我们使用缓存中现有的数据初始化状态,如果缓存中没有数据,则返回一个空数组。这是使用钩子useQueryClient及其属性getQueryData实现的。
export const useGetUsersObserver = () => {
const get_users = useGetUsers()
const queryClient = useQueryClient()
const [users, setUsers] = useState<User[]>(() => {
const data = queryClient.getQueryData<User[]>([key])
return data ?? []
})
return {
...get_users,
data: users,
}
}
- 现在我们将使用 effect 来处理观察者。在 effect 中,我们创建一个 QueryObserver 的新实例,它需要两个参数:queryClient和一个需要queyKey 的对象,该对象用于确定要监视缓存的哪个部分。
useEffect(() => {
const observer = new QueryObserver<User[]>(queryClient, { queryKey: [key] })
}, [])
- 现在我们需要订阅观察者,因此我们执行观察者的subscribe属性。subscribe 会接收一个回调函数,该回调函数返回一个对象,该对象与返回类似useQuery钩子函数的属性基本相同。因此,我们会验证 data 属性中是否有数据,然后使用新数据更新状态。
useEffect(() => {
const observer = new QueryObserver<User[]>(queryClient, { queryKey: [key] })
const unsubscribe = observer.subscribe(result => {
if (result.data) setUsers(result.data)
})
}, [])
- 请记住,一个好的做法是在拆卸组件时取消订阅。
useEffect(() => {
const observer = new QueryObserver<User[]>(queryClient, { queryKey: [key] })
const unsubscribe = observer.subscribe(result => {
if (result.data) setUsers(result.data)
})
return () => {
unsubscribe()
}
}, [])
这个新的自定义钩子看起来是这样的。
export const useGetUsersObserver = () => {
const get_users = useGetUsers()
const queryClient = useQueryClient()
const [users, setUsers] = useState<User[]>(() => {
const data = queryClient.getQueryData<User[]>([key])
return data ?? []
})
useEffect(() => {
const observer = new QueryObserver<User[]>(queryClient, { queryKey: [key] })
const unsubscribe = observer.subscribe(result => {
if (result.data) setUsers(result.data)
})
return () => {
unsubscribe()
}
}, [])
return {
...get_users,
data: users,
}
}
现在,我们只需要在需要获取这些数据的组件中使用它即可。例如在ViewUser组件中。
不要忘记导入useGetUsersObserver。
export const ViewUser = ({ id }: Pick<User, 'id'>) => {
// const get_users = useGetUsers()
const get_users = useGetUsersObserver()
const user_selected = get_users.data?.find(user => user.id === +id)
if (!user_selected) return null
return (
<>
<h1>Edit user: {id}</h1>
<span>User name: <b>{user_selected?.name}</b></span>
</>
)
}
现在,如果您尝试更新数据或删除数据,您将看到一旦请求成功,ViewUser组件也将如何更新。
这样,我们将使用 React Query 缓存作为状态管理器来完成 CRUD。
📢结论。
React Query 是一个非常强大的库,它确实能帮助我们处理请求。但现在你可以进一步扩展它,将它用作状态管理器,这或许是另一种选择。
我希望你喜欢这篇文章,也希望我能帮助你扩展对 React Query 的了解。
如果您知道有关如何使用 React Query 管理状态的任何其他不同或更好的方法,请随时在评论中告诉我。
如果您有兴趣就某个项目与我联系,欢迎查看我的投资组合!富兰克林·马丁内斯·卢卡斯
🔵 别忘了在推特上关注我:@Frankomtz361
📢 演示。
https://rq-state-management.netlify.app/
📢 源代码。
https://github.com/Franklin361/state-management-react-query
鏂囩珷鏉ユ簮锛�https://dev.to/franklin030601/managing-state-with-react-query-1842