๐ŸŽ‰ berenickt ๋ธ”๋กœ๊ทธ์— ์˜จ ๊ฑธ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค. ๐ŸŽ‰
Front
Tanstack Query
05-React Native

1. React Native

React Query๋Š” React DOM์—์„œ๋งŒ ์ง€์›๋˜๋Š” DevTools๋ฅผ ์ œ์™ธํ•˜๊ณ  ๋™์ž‘ํ•œ๋‹ค.

์‹œ๋„ํ•ด ๋ณผ ๋งŒํ•œ ์„œ๋“œํŒŒํ‹ฐ Flipper ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์žˆ๋‹ค https://github.com/bgaleotti/react-query-native-devtools

์‹œ๋„ํ•ด ๋ณผ ๋งŒํ•œ ์„œ๋“œํŒŒํ‹ฐ Reactotron ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์žˆ๋‹ค. https://github.com/hsndmr/reactotron-react-query

๐Ÿ’ก ์„œ๋“œํŒŒํ‹ฐ(Third Party)์˜ ์‚ฌ์ „์  ์ •์˜๋Š” โ€˜์ œ3์žโ€™๋ฅผ ์˜๋ฏธ

IT์—…๊ณ„์—์„œ ์„œ๋“œํŒŒํ‹ฐ๋Š” ์–ด๋– ํ•œ ๋ถ„์•ผ์—์„œ ์ฒ˜์Œ ๊ฐœ๋ฐœํ•˜๊ฑฐ๋‚˜ ์›์ฒœ๊ธฐ์ˆ ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฒŒ ์•„๋‹Œ, ์›์ฒœ๊ธฐ์ˆ ๊ณผ ํ˜ธํ™˜๋˜๋Š” ์ƒํ’ˆ์„ ์ถœ์‹œํ•˜๊ฑฐ๋‚˜ ํ•ด๋‹น ๊ธฐ์ˆ ์„ ์ด์šฉํ•œ ํŒŒ์ƒ์ƒํ’ˆ์„ ์ƒ์‚ฐํ•˜๋Š” ํšŒ์‚ฌ


2. ์˜จ๋ผ์ธ ์ƒํƒœ ๊ด€๋ฆฌ

React Query๋Š” ์ด๋ฏธ ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ ์žฌ์—ฐ๊ฒฐ ์‹œ ์ž๋™ ์žฌ์„ค์ •์„ ์ง€์›ํ•œ๋‹ค. React Native์—์„œ๋„ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด, ์•„๋ž˜ ์˜ˆ์™€ ๊ฐ™์ด React Query onlineManager๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค:

1
import NetInfo from '@react-native-community/netinfo'
2
import { onlineManager } from '@tanstack/react-query'
3
4
onlineManager.setEventListener(setOnline => {
5
return NetInfo.addEventListener(state => {
6
setOnline(!!state.isConnected)
7
})
8
})

3. App focus์—์„œ Refetch

์›น์˜ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๋Œ€์‹ ์—, React Native๋Š” AppState module ํ†ตํ•ด focus ์ •๋ณด๋ฅผ ์ œ๊ณตํ•œ๋‹ค. AppState โ€œchangeโ€ ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•ด, ์•ฑ ์ƒํƒœ๊ฐ€ โ€œactiveโ€๋กœ ๋ณ€๊ฒฝ๋  ๋•Œ ์—…๋ฐ์ดํŠธ๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•  ์ˆ˜ ์žˆ๋‹ค:

1
import { useEffect } from 'react'
2
import { AppState, Platform } from 'react-native'
3
import type { AppStateStatus } from 'react-native'
4
import { focusManager } from '@tanstack/react-query'
5
6
function onAppStateChange(status: AppStateStatus) {
7
if (Platform.OS !== 'web') {
8
focusManager.setFocused(status === 'active')
9
}
10
}
11
12
useEffect(() => {
13
const subscription = AppState.addEventListener('change', onAppStateChange)
14
15
return () => subscription.remove()
16
}, [])

4. Screen focus์—์„œ Refetch

์ผ๋ถ€ ์ƒํ™ฉ๋“ค์—์„œ, React Native Screen์ด ๋‹ค์‹œ ํฌ์ปค์‹ฑ๋  ๋•Œ, query๋ฅผ refetchํ•˜๊ธฐ๋ฅผ ์›ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด custom hook์€ Screen์ด ๋‹ค์‹œ ํฌ์ปค์‹ฑ๋  ๋•Œ, ์ œ๊ณต๋œ refetch ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

1
import React from 'react'
2
import { useFocusEffect } from '@react-navigation/native'
3
4
export function useRefreshOnFocus<T>(refetch: () => Promise<T>) {
5
const firstTimeRef = React.useRef(true)
6
7
useFocusEffect(
8
React.useCallback(() => {
9
if (firstTimeRef.current) {
10
firstTimeRef.current = false
11
return
12
}
13
14
refetch()
15
}, [refetch]),
16
)
17
}

์œ„์˜ ์ฝ”๋“œ์—์„œ, refetch๋Š” () ์ฒ˜์Œ์— ์ƒ๋žต๋œ๋‹ค.
(,์™œ๋‚˜ํ•˜๋ฉด useFocusEffect๊ฐ€ screen focus๊ฐ€ ์ถ”๊ฐ€๋œ mount์—์„œ ์ฝœ๋ฐฑ์„ ํ˜ธ์ถœํ•˜๊ธฐ ๋•Œ๋ฌธ์—,)


5. out of focus Screens์—์„œ re-renders ๋ง‰๊ธฐ

์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ํฌํ•จํ•œ ์ผ๋ถ€ ์ƒํ™ฉ์—์„œ๋Š”, React Native ํ™”๋ฉด์ด ํฌ์ปค์Šค๋ฅผ ๋ฒ—์–ด๋‚  ๋•Œ, re-renders์„ ์ค‘์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ๋‹ฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด, NotifyOnChangeProps ์ฟผ๋ฆฌ ์˜ต์…˜๊ณผ ํ•จ๊ป˜, @react-navigation/native์˜ FocusEffect๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด ์ปค์Šคํ…€ Hook์€ () NotifyOnChangeProps ์˜ต์…˜์„ ์ œ๊ณตํ•œ๋‹ค. (ํ™”๋ฉด์ด ์ดˆ์ ์„ ๋ฒ—์–ด๋‚  ๋•Œ๋งˆ๋‹ค ๋นˆ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•˜๋Š”) (NotifyOnChangeProps - ํ•ด๋‹น ์‹œ๋‚˜๋ฆฌ์˜ค์— ๋Œ€ํ•œ ์žฌ๋ Œ๋”๋ง์„ ํšจ๊ณผ์ ์œผ๋กœ ๋ง‰๋Š”)

ํ™”๋ฉด์ด ๋‹ค์‹œ ์ดˆ์ (focus)์„ ๋งž์ถœ ๋•Œ๋งˆ๋‹ค, ์žฌ์ •์ƒ์œผ๋กœ ๋Œ์•„๊ฐ‘๋‹ˆ๋‹ค.

1
import React from 'react'
2
import { NotifyOnChangeProps } from '@tanstack/query-core'
3
import { useFocusEffect } from '@react-navigation/native'
4
5
export function useFocusNotifyOnChangeProps(notifyOnChangeProps?: NotifyOnChangeProps) {
6
const focusedRef = React.useRef(true)
7
8
// ์—ฌ๊ธฐ
9
useFocusEffect(
10
React.useCallback(() => {
11
focusedRef.current = true
12
13
return () => {
14
focusedRef.current = false
15
}
16
}, []),
17
)
18
19
return () => {
20
if (!focusedRef.current) {
21
return []
22
}
23
24
if (typeof notifyOnChangeProps === 'function') {
25
return notifyOnChangeProps()
26
}
27
28
return notifyOnChangeProps.current
29
}
30
}

์œ„ ์ฝ”๋“œ์—์„œ, useFocusEffect๋Š” () ์‚ฌ์šฉ๋œ๋‹ค. (์ฝœ๋ฐฑ์กฐ๊ฑด์œผ๋กœ ์‚ฌ์šฉํ•  ๊ธฐ์ค€๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋Š”๋ฐ,)

์ด ์ธ์ž(argument)๊ฐ€ () ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด ์ฐธ์กฐ๋ฅผ ๊ฐ์‹ผ๋‹ค.
(๋ฐ˜ํ™˜๋œ ์ฝœ๋ฐฑ์ด ํ•ญ์ƒ ๋™์ผํ•œ ์ฐธ์กฐ๋ฅผ ์œ ์ง€ํ•œ๋‹ค๋Š” ๊ฒƒ์„,)

์‚ฌ์šฉ ์˜ˆ:

1
function MyComponent() {
2
const notifyOnChangeProps = useFocusNotifyOnChangeProps()
3
4
const { dataUpdatedAt } = useQuery({
5
queryKey: ['myKey'],
6
queryFn: async () => {
7
const response = await fetch('https://api.github.com/repos/tannerlinsley/react-query')
8
return response.json()
9
},
10
notifyOnChangeProps,
11
})
12
13
return <div>DataUpdatedAt: {dataUpdatedAt}</div>
14
}