下面就是一個簡單的從服務端獲取數據的案例javascript
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 可以更高效的幫你管理服務端的狀態。java
這是一個適用於 React Hooks 的請求庫。 這個庫將幫助你獲取、同步、更新和緩存你的遠程數據, 提供兩個簡單的 hooks,就能完成增刪改查等操做React-Query 使用聲明式管理服務端狀態,能夠使用零配置開箱即用地處理緩存,後臺更新和陳舊數據。無需繁瑣的配置,編寫useReduce,以及維護全局狀態,只要知道如何使用 Promise 或 async / await ,傳遞一個可解析數據(或引起錯誤)的函數,剩下的交給 React-Query 就行了,使用更少的代碼得到更高的效率.react
安裝 React-Queryios
$ npm i react-query
# or
$ yarn add react-query
複製代碼
下面咱們用 React-Query改寫git
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還爲咱們作了以下工做:github
如何全局配置呢?以下:shell
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)npm
基本使用方法axios
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>
)
}
複製代碼
傳遞參數segmentfault
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')
},
})
複製代碼
導入開發工具
import { ReactQueryDevtools } from 'react-query/devtools'
複製代碼
默認狀況下,當process.env.NODE ENV === 'production' 時開啓 Devtools ,沒必要擔憂構建時須要排除他們
浮動模式下開啓,會將devtools做爲固定的浮動元素安裝在開發的應用程序中,並在屏幕一角提供一個切換按鈕以顯示和隱藏devtools。
儘量將如下代碼放在您的React應用程序中。 它離頁面根目錄越近,效果越好!
import { ReactQueryDevtools } from 'react-query/devtools'
function App() {
return (
<QueryClientProvider client={queryClient}> {/* The rest of your application */} <ReactQueryDevtools initialIsOpen={false} /> </QueryClientProvider>
)
}
複製代碼
React-Query 周邊生態知足平常開發需求,支持 TypeScript ,GraphQL 。並可在 React Native 中使用,同時也兼容服務端渲染方案 SSR 支持 Next.js 的使用
- react-query 對於 mutation 可以使用 hooks,支持更多選項 (如 keepPreviousData),功能更多,更適合 API 複雜的項目
- swr 相對輕量,可隨處使用,不須要在父組件設置 Provider 。更加輕便
官網相關方案對比表格
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 能夠更加高效的管理來自服務端的請求狀態,用更少的代碼實現較爲複雜的需求,讓你的狀態管理更優雅。
參考連接: