Overview of React Query

notion image
![stale](images/stale.png)
--- title: 'Overview of React Query' author: 'Hun Im' date: 2023-01-12T13:53:35+09:00 category: ['POSTS'] tags: ['Javascript', 'React'] og_image: "/images/gamer.png" keywords: ['Javascript', 'React'] --- - React Query efficiently manages data through unique keys, preventing duplicate fetch calls and improving performance. For example, if you create a fetch function via a custom hook, it will be called each time it's used, leading to unnecessary calls for the same data. **Query Keys** React Query manages cached queries using query keys, organizing complex objects and long strings as arrays. Since these keys are serializable, they serve as unique identifiers for the cached data. ```js useQuery(['todos'], () => {...}) useQuery(['todos', 5, { preview : true }], () => {...}) useQuery(['todos', { type: 'done'}], () => {...}) ``` - By requesting desired data in an array, you create a unique key for caching the data. Example usage: ```jsx const Products = () => { const [checked, setChecked] = useState(false) const { isLoading, error, data: products, } = useQuery(['products', checked], async () => { return fetch(`data/${checked ? 'sale_' : ''}products.json`).then((res) => res.json() ) }) } ``` - A fetch request is made when the `checked` state changes. **Issues** Although it may seem like caching is effective, you might notice in the console that duplicate data (such as already rendered UI or unchanged data) is repeatedly requested. **Key Points of React Query** React Query is aggressively set up but also provides options for fine-tuning, which can be difficult for beginners to understand and debug. - By default, `useQuery` and `useInfiniteQuery` consider cached data as stale (stale: inactive or expired). - The `staleTime` option can be set globally to control this behavior : While `staleTime` is set, cached query data will not be refetched Refetching can occur under the following conditions: 1. New instances of the query mount 2. The window is refocused 3. The network is reconnected 4. The query is optionally configured with a refetch interval - If refetching happens unexpectedly, consider the window focus as a possible cause: 1. The `refetchOnWindowFocus` feature is enabled by default, which can cause refetching when switching between tabs, especially when using dev tools. -> Adjust settings for `refetchOnMount`, `refetchOnWindowFocus`, `refetchOnReconnect`, and `refetchInterval`. 2. If `useQuery` or `useInfiniteQuery` are not used for a while, they will become inactive. After 5 minutes in this state, they will be automatically garbage collected. -> Change the `cacheTime` setting to a value greater than `1000 * 60 * 5 (5 minutes)`. 3. When a query fails, it retries three times by default, with increasing intervals. -> Use the `retry` and `retryDelay` settings to control this behavior. **Data Communication Sequence Based on Stale State** ![stale](images/stale.png) **Updating Data** When updating, you can invalidate the existing query, which will automatically trigger a refetch. ```jsx const client = useQueryClient() <button onClick={()=>client.invalidateQueries(['products', false])}>์ •๋ณด ์—…๋ฐ์ดํŠธ ํ•˜๊ธฐ!</button> ``` **Transitioning from Redux to React Query** Link : [Transitioning from Redux to React Query][https://youtu.be/hcvcb36wzzk]
--- title: 'React Query ์ •๋ฆฌ' author: '์ž„ํ›ˆ' date: 2023-01-12T13:53:35+09:00 category: ['POSTS'] tags: ['Javascript', 'React'] og_image: "/images/gamer.png" keywords: ['Javascript', 'React'] --- - ํŠน์ •ํ•œ ํ‚ค๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋ฏ€๋กœ, ์ค‘๋ณต ํ˜ธ์ถœ์ด ๋  ๋•Œ, ํ•œ๋ฒˆ๋งŒ ํ˜ธ์ถœ ํ•˜๋ฏ€๋กœ ์„ฑ๋Šฅ์— ์ข‹๋‹ค. ex) custom hook์„ ํ†ตํ•ด, fetch๋ฌธ์„ ๋งŒ๋“ค์–ด, ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด, ์‚ฌ์šฉ ๋  ๋•Œ ๋งˆ๋‹ค ํ˜ธ์ถœ์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ผ๋„ ๋ถˆํ•„์š”ํ•œ ํ˜ธ์ถœ์„ ํ•˜๊ฒŒ ๋œ๋‹ค. **Query Keys** React Query๋Š” ์บ์‹ฑํ™”๋œ ์ฟผ๋ฆฌ๋“ค์„ ์ฟผ๋ฆฌ ํ‚ค๋กœ์จ ๊ด€๋ฆฌํ•œ๋‹ค. ๋ณต์žกํ•œ ๊ฐ์ฒด์™€ ๋ณต์žกํ•˜๊ณ  ๋งŽ์€ ๋ฌธ์ž์—ด๋“ค์„ ๋ฐฐ์—ด๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๋‹ค. Serializableํ•˜๋ฏ€๋กœ, ์œ ๋‹ˆํฌํ•œ ์ฟผ๋ฆฌ ๋ฐ์ดํ„ฐ๋กœ ๊ด€๋ฆฌ๋œ๋‹ค. ```js useQuery(['todos'], () => {...}) useQuery(['todos', 5, { preview : true }], () => {...}) useQuery(['todos', { type: 'done'}], () => {...}) ``` - ์›ํ•˜๋Š” ์š”์ฒญ์„ ๋ฐฐ์—ด๋กœ ์š”์ฒญํ•˜๋ฉด, ์œ ๋‹ˆํฌํ•œ ํ‚ค๋กœ์จ ์บ์‹ฑํ™”ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค. ํ™œ์šฉ ์˜ˆ์ œ ```jsx const Products = () => { const [checked, setChecked] = useState(false) const { isLoading, error, data: products, } = useQuery(['products', checked], async () => { return fetch(`data/${checked ? 'sale_' : ''}products.json`).then((res) => res.json() ) }) } ``` - checked ์ƒํƒœ๊ฐ€ ๋ณ€ํ™” ํ•  ๋•Œ fetch์š”์ฒญ์„ ํ•œ๋‹ค. **๋ฌธ์ œ์ ** ์บ์‹ฑํ™”๋˜์–ด์žˆ๋‹ค๊ณ  ์ƒ๊ฐ ํ•˜๋Š”๋ฐ console๋กœ ํ™•์ธํ•ด๋ณด๋ฉด, ์ค‘๋ณต ๋ฐ์ดํ„ฐ(์ด๋ฏธ ๋ Œ๋”๋ง ์ฒ˜๋ฆฌ ๋œ UI๋‚˜ ๋ณ€ํ™”์—†๋Š” ๋ฐ์ดํ„ฐ ๋“ฑ๋“ฑ์˜ ์ƒํ™ฉ) ๋งค์ˆœ๊ฐ„ ๋ฐ˜๋ณต ์š”์ฒญ ํ•˜๋Š” ๋ฌธ์ œ์  **React Query์˜ ์ค‘์š”ํ•œ์ ** React Query๋Š” ๊ณต๊ฒฉ์ ์œผ๋กœ ์„ค์ •๋˜์–ด์žˆ์ง€๋งŒ, ์ž˜ ๋ถ„๋ณ„ ํ•˜๊ฒŒ๋„ ์„ค์ •๋˜์–ด์žˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ ์€ ์ž˜๋ชจ๋ฅด๋Š” ๋‰ด๋น„๋“ค์—๊ฒŒ ๋ฐฐ์šฐ๊ธฐ ์–ด๋ ต๊ณ  ๋””๋ฒ„๊น…ํ•˜๊ธฐ ์–ด๋ ต๊ฒŒ ์„ค์ •๋˜์–ด์žˆ๋‹ค. - `useQuery`, `useInfiniteQuery`๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ staleํ•œ ์บ์‹œ๋ฐ์ดํ„ฐ๋กœ ๊ฐ„์ฃผ๋œ๋‹ค. (stale : ์ฃฝ์€ ์ƒํƒœ) - `staleTime`์ด๋ผ๋Š” ์˜ต์…˜์„ ์ „์—ญ์ ์œผ๋กœ ์„ค์ •ํ•ด์ฃผ๋ฏ€๋กœ์จ ์ปจํŠธ๋กค ํ•  ์ˆ˜ ์žˆ๋‹ค. : `staleTime`์ด ์„ค์ •๋˜์–ด ์žˆ๋Š” ๋™์•ˆ์—๋Š” ์บ์‹ฑํ™”๋œ query ๋ฐ์ดํ„ฐ๋ฅผ refetchํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค. 1. New instances of the query mount 2. The window is refocused 3. The network is reconnected 4. The query is optionally configured with a refetch interval - refetch๊ฐ€ ๋‚ด ์˜๋„์™€ ์ƒ๊ด€์—†์ด ๋  ๋•Œ๋Š”, window๊ฐ€ focus๊ฐ€ ๋  ๋•Œ๋ฅผ ์˜์‹ฌํ•ด๋ณด์ž 1. `refetchOnWindowFocus`๊ธฐ๋Šฅ์ด ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ผœ์ ธ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•œ๋‹ค. dev ํ™˜๊ฒฝ์ผ ๋•Œ, devtools๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด, ๋˜ํ•œ window๋ฅผ ์™”๋‹ค๊ฐ”๋‹ค ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, refetch๊ฐ€ ๊ณ„์†ํ•ด์„œ ์ผ์–ด๋‚˜๊ฒŒ ๋œ๋‹ค. -> `refetchOnMount`,`refetchOnWindowFocus`, `refetchOnReconnect`,`refetchInterval` ์„ค์ •์„ ๋ฐ”๊พธ์ž 2. `useQuery`, `useInfiniteQuery`๋ฅผ ๋”์ด์ƒ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด, `inactive`์ƒํƒœ๊ฐ€ ๋œ๋‹ค. ์ด ์ƒํƒœ๊ฐ€ 5๋ถ„๊ฐ„ ์ง€์†๋˜๋ฉด ์ž๋™์ ์œผ๋กœ garbage collect๊ฐ€ ๋œ๋‹ค. -> `cacheTime`์˜ ์„ค์ •์„ ๋ฐ”๊พธ์ž. `1000 * 60 * 5` ์ด์ƒ์˜ ๊ฐ’(5๋ถ„์ด์ƒ)์„ ์ค€๋‹ค. 3. Query๊ฐ€ ์‹คํŒจํ•  ๋•Œ, 3๋ฒˆ ์žฌ๋ฐ˜๋ณต ์‹คํ–‰์„ ํ•œ๋‹ค. 3๋ฒˆ ๋ฐ˜๋ณต(๊ธฐ๋ณธ๊ฐ’ : 3)ํ•  ๋•Œ, ๊ฐ„๊ฒฉ์ด ์ ์  ๊ธธ์–ด์ง. -> `retry`, `retryDelay`๋กœ ์„ค์ •ํ•œ๋‹ค. **stale ์ƒํƒœ์— ๋”ฐ๋ฅธ ๋ฐ์ดํ„ฐ ํ†ต์‹  ์ˆœ์„œ** ![stale](images/stale.png) **์—…๋ฐ์ดํŠธ ํ•˜๊ธฐ** ์—…๋ฐ์ดํŠธ ๋˜๋ฉด ๊ธฐ์กด ์ฟผ๋ฆฌ๋Š” invalidate ์‹œ์ผœ์ฃผ๋ฉด ๋‹ค์‹œ ์ €์ ˆ๋กœ fetch๋ฅผ ํ•˜๊ฒŒ ๋œ๋‹ค. ```jsx const client = useQueryClient() <button onClick={()=>client.invalidateQueries(['products', false])}>์ •๋ณด ์—…๋ฐ์ดํŠธ ํ•˜๊ธฐ!</button> ``` **Redux -> React-Query ์ „ํ™˜** Link : [Redux -> React-Query ์ „ํ™˜][https://youtu.be/hcvcb36wzzk]