下面就是一个简单的从服务端获取数据的案例
function App() {
const [data, updateData] = useState(null);
const [isError, setError] = useState(false);
const [isLoading, setLoading] = useState(false);
useEffect(async () => {
setError(false);
setLoading(true);
try {
const data = await axios.get('/api/user');
updateData(data);
} catch(e) {
setError(true);
}
setLoading(false);
}, [])
}
在使用 React Hooks 编写组件时,我们常需要手动维护来自服务器的处理状态。在日常开发中引起一些麻烦。 处理异步数据时,我们需要考虑很多事情,例如更新,缓存或重新获取。 使用 React-Query 能够更高效的帮你管理服务端的状态。
React-Query 是什么
这是一个适用于 React Hooks 的请求库。 这个库将帮助你获取、同步、更新和缓存你的远程数据, 提供两个简单的 hooks,就能完成增删改查等操作React-Query 使用声明式管理服务端状态,可以使用零配置开箱即用地处理缓存,后台更新和陈旧数据。无需繁琐的配置,编写useReduce,以及维护全局状态,只要知道如何使用 Promise 或 async / await ,传递一个可解析数据(或引发错误)的函数,剩下的交给 React-Query 就好了,使用更少的代码获得更高的效率.
开始使用 React-Query
安装 React-Query
$ npm i react-query
# or
$ yarn add react-query
下面我们用 React-Query改写
import { useQuery } from 'react-query'
function App() {
const {data, isLoading, isError} = useQuery('userData', () => axios.get('/api/user'));
if (isLoading) {
return <div>loading</div>;
}
return (
<ul>
{data.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
)
}
例子中 "userData" 字符串就是这个 query 独一无二的key。 可以看到,React-Query封装了完整的请求中间状态(isLoading、isError...)。 不仅如此,React-Query还为我们做了如下工作:
- 多个组件请求同一个query时只发出一个请求
- 缓存数据失效/更新策略(判断缓存合适失效,失效后自动请求数据)
- 对失效数据垃圾清理
React-Query 中的三个重要知识点
常用参数配置
- staleTime 重新获取数据的时间间隔 默认0
- cacheTime 数据缓存时间 默认 1000 60 5 5分钟
- retry 失败重试次数 默认 3次
- refetchOnWindowFocus 窗口重新获得焦点时重新获取数据 默认 false
- refetchOnReconnect 网络重新链接
- refetchOnMount 实例重新挂载
- enabled 如果为“false”的化,“useQuery”不会触发,需要使用其返回的“refetch”来触发操作
如何全局配置呢?如下:
import { ReactQueryConfigProvider, ReactQueryProviderConfig } from 'react-query';
const queryConfig: ReactQueryProviderConfig = {
/**
* refetchOnWindowFocus 窗口获得焦点时重新获取数据
* staleTime 过多久重新获取服务端数据
* cacheTime 数据缓存时间 默认是 5 * 60 * 1000 5分钟
*/
queries: {
refetchOnWindowFocus: true,
staleTime: 5 * 60 * 1000,
retry: 0
},
};
ReactDOM.render(
<ReactQueryConfigProvider config={queryConfig}>
<App />
</ReactQueryConfigProvider>
document.getElementById('root')
);
也可以单独配置,如下:
function Todos() {
// 第三个参数即可传参了
// "enabled"参数为false的化,不会自动发起请求,而是需要调用“refetch”来触发
const {
isIdle,
isLoading,
isError,
data,
error,
refetch,
isFetching,
} = useQuery('todos', fetchTodoList, {
enabled: false,
})
return (
<>
<button onClick={() => refetch()}>Fetch Todos</button>
{isIdle ? (
'Not ready...'
) : isLoading ? (
<span>Loading...</span>
) : isError ? (
<span>Error: {error.message}</span>
) : (
<>
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
<div>{isFetching ? 'Fetching...' : null}</div>
</>
)}
</>
)
}
数据查询与操作
useQuery(查)查询数据 (Get)
基本使用方法
function Todos() {
// useQuery的第一个参数,作为useQuery查询的唯一标识,该值唯一
// 可以是string、array、object
// string -> useQuery('todos', ...) queryKey === ['todos']
// array -> useQuery(['todo', 5], ...) queryKey === ['todo', 5]
// object -> useQuery(['todo', 5, { preview: true }], ...) queryKey === ['todo', 5, { preview: true }]
const { isLoading, isError, data, error } = useQuery('todos', fetchTodoList)
if (isLoading) {
return <span>Loading...</span>
}
if (isError) {
return <span>Error: {error.message}</span>
}
// also status === 'success', but "else" logic works, too
return (
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
)
}
传递参数
function Todos({ completed }) {
// useQuery(['todo', { status: 1, page: 1 }], ...) queryKey === ['todo', { status: 1, page: 1 }]
// 传递参数给“fetchTodoList”使用
const queryInfo = useQuery(['todos', { status: 1, page: 1 }], fetchTodoList)
}
// 函数参数
// key -> “todos”
// status -> 1 page -> 1
function fetchTodoList(key, { status, page }) {
return new Promise()
// ...
}
该库还实现了常用的查询操作:
- 分页查询
- 无限滚动
useMutation(增、改、删)操作数据 (Post,Delete,Patch,Put
// 当“mutate()”被调用时,执行“pingMutation”
const PingPong = () => {
const [mutate, { status, data, error }] = useMutation(pingMutation)
const onPing = async () => {
try {
const data = await mutate()
console.log(data)
} catch {
}
}
return <button onClick={onPing}>Ping</button>
}
传递参数
// "mutate({title})"就会将参数“title”传递给“createTodo”函数了
const createTodo = ({ title }) => {
console.log("title ", title)
}
const CreateTodo = () => {
const [title, setTitle] = useState('')
const [mutate] = useMutation(createTodo)
const onCreateTodo = async e => {
e.preventDefault()
try {
await mutate({ title })
// Todo was successfully created
} catch (error) {
// Uh oh, something went wrong
}
}
return (
<form onSubmit={onCreateTodo}>
<input
type="text"
value={title}
onChange={e => setTitle(e.target.value)}
/>
<br />
<button type="submit">Create Todo</button>
</form>
)
}
清除缓存
每当我们编辑完一篇文章,返回列表页面,如果不清除缓存,那么数据还是缓存的数据,所以需要清除缓存,使得“userQuery”失效,回到列表页的时候重新拉取最新数据
参考代码如下
import { useMutation, useQueryCache } from 'react-query'
const queryCache = useQueryCache()
const [mutate] = useMutation(addTodo, {
onSuccess: () => {
// invalidateQueries 的匹配规则
// eg:
// queryCache.invalidateQueries('todos') 那么如下两个`query key`都会被匹配到,匹配到的缓存都会失效
// const todoListQuery = useQuery('todos', fetchTodoList)
// const todoListQuery = useQuery(['todos', { page: 1 }], fetchTodoList)
queryCache.invalidateQueries('todos')
queryCache.invalidateQueries('reminders')
},
})
Devtools 配套开发工具
导入开发工具
import { ReactQueryDevtools } from 'react-query/devtools'
默认情况下,当process.env.NODE ENV === 'production' 时开启 Devtools ,不必担心构建时需要排除他们
浮动模式下开启,会将devtools作为固定的浮动元素安装在开发的应用程序中,并在屏幕一角提供一个切换按钮以显示和隐藏devtools。
import { ReactQueryDevtools } from 'react-query/devtools'
function App() {
return (
<QueryClientProvider client={queryClient}>
{/* The rest of your application */}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
)
}
React-Query 周边生态
React-Query 周边生态满足日常开发需求,支持 TypeScript ,GraphQL 。并可在 React Native 中使用,同时也兼容服务端渲染方案 SSR 支持 Next.js 的使用
其他方案对比
官网相关方案对比表格
React Query | SWR (Website) | Apollo Client (Website) | RTK-Query (Website) | Github Repo / Stars | 18K | 16K | 16K | 372 | Platform Requirements | React | React | React, GraphQL | Redux | Their Comparison | (none) | (none) | Comparison | Supported Query Syntax | Promise, REST, GraphQL | Promise, REST, GraphQL | GraphQL | Promise, REST, GraphQL | Supported Frameworks | React | React | React + Others | Any | Supported Query Keys | JSON | JSON | GraphQL Query | JSON | Query Key Change Detection | Deep Compare (Stable Serialization) | Referential Equality (===) | Deep Compare (Unstable Serialization) | Referential Equality (===) | Query Data Memoization Level | Query + Structural Sharing | Query | Query + Entity + Structural Sharing | Query | Bundle Size | 11.2KB | 5.0KB | 33.9KB | 10.3KB | API Definition | On-Use, Declarative | On-Use | GraphQL Schema | Declarative | Queries | ✅ | ✅ | ✅ | ✅ | Caching | ✅ | ✅ | ✅ | ✅ | Devtools | ✅ | ? | ✅ | ✅ | Polling/Intervals | ✅ | ✅ | ✅ | ✅ | Parallel Queries | ✅ | ✅ | ✅ | ✅ | Dependent Queries | ✅ | ✅ | ✅ | ✅ | Paginated Queries | ✅ | ✅ | ✅ | ✅ | Infinite Queries | ✅ | ✅ | ✅ | ? | Bi-directional Infinite Queries | ✅ | ? | ? | ? | Infinite Query Refetching | ✅ | ✅ | ? | ? | Lagged Query Data1 | ✅ | ? | ? | ✅ | Selectors | ✅ | ? | ✅ | ✅ | Initial Data | ✅ | ✅ | ✅ | ✅ | Scroll Recovery | ✅ | ✅ | ✅ | ✅ | Cache Manipulation | ✅ | ✅ | ✅ | ✅ | Outdated Query Dismissal | ✅ | ✅ | ✅ | ✅ | Render Optimization2 | ✅ | ? | ? | ✅ | Auto Garbage Collection | ✅ | ? | ? | ✅ | Mutation Hooks | ✅ | ? | ✅ | ✅ | Offline Mutation Support | ✅ | ? | ? | ? | Prefetching APIs | ✅ | ? | ✅ | ✅ | Query Cancellation | ✅ | ? | ? | ? | Partial Query Matching3 | ✅ | ? | ? | ✅ | Stale While Revalidate | ✅ | ✅ | ? | ✅ | Stale Time Configuration | ✅ | ? | ? | ? | Pre-usage Query/Mutation Configuration4 | ✅ | ? | ? | ✅ | Window Focus Refetching | ✅ | ✅ | ? | ? | Network Status Refetching | ✅ | ✅ | ✅ | ? | General Cache Dehydration/Rehydration | ✅ | ? | ✅ | ✅ | Offline Caching | ✅ (Experimental) | ? | ✅ | ? | React Suspense (Experimental) | ✅ | ✅ | ? | ? | Abstracted/Agnostic Core | ✅ | ? | ✅ | ✅ | Automatic Refetch after Mutation5 | ? | ? | ✅ | ✅ | Normalized Caching6 | ? | ? | ✅ | ? |
---|
更多查看
总结
使用 React-Query 可以更加高效的管理来自服务端的请求状态,用更少的代码实现较为复杂的需求,让你的状态管理更优雅。
参考链接:
React-Query 官网
用 react-query 解决你一半的状态管理问题
react-query
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!