fetch
함수만 사용하면, 로딩 상태나 오류 처리, 캐싱 등 여러 번 반복되는 패턴을 매번 직접 구현해야 해서 번거롭습니다.
이를 해결하기 위해 npx expo install @tanstack/react-query
npm i -D @tanstack/eslint-plugin-query
QueryClient
를 만들고, 앱의 최상단 컴포넌트를 QueryClientProvider
로 감싸야 합니다.import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
export default function App() {
return (
<QueryClientProvider client={queryClient}>
{/* 앱 컴포넌트들 */}
</QueryClientProvider>
);
}
/users
)를 가져와서 리스트로 표시해보겠습니다.import { useQuery } from '@tanstack/react-query';
import axios from 'axios';
import { FlatList, Text, View } from 'react-native';
const fetchUsers = async () => { // 정보를 fetch하는 함수
const res = await axios.get('https://jsonplaceholder.typicode.com/users');
return res.data;
};
export default function UserList() {
const { data, isLoading, isError, error } = useQuery({
queryKey: ['users'], // 쿼리의 고유 식별자
queryFn: fetchUsers, // 정보를 fetch하는 함수
});
if (isLoading) return <Text>불러오는 중...</Text>;
if (isError) return <Text>오류 발생: {(error as Error).message}</Text>;
return (
<FlatList
data={data}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => <Text>{item.name}</Text>}
/>
);
}
queryKey
는 요청을 구분하고 캐시를 적용하는 데 매우 중요한 역할을 합니다. 배열 형태로 작성하며, 특정 파라미터에 따라 다르게 구성할 수 있습니다.useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
});
userId
가 다르면 다른 쿼리로 인식되어 각기 다른 캐시를 사용하게 됩니다.AppState
와 focusManager
를 사용합니다:import { useEffect } from 'react';
import { AppState, Platform } from 'react-native';
import { focusManager } from '@tanstack/react-query';
function onAppStateChange(status) {
if (Platform.OS !== 'web') {
focusManager.setFocused(status === 'active');
}
}
useEffect(() => {
const subscription = AppState.addEventListener('change', onAppStateChange);
return () => subscription.remove();
}, []);
useFocusEffect
를 활용하여 화면에 들어올 때 데이터를 갱신할 수 있습니다:import { useFocusEffect } from '@react-navigation/native';
import { useCallback } from 'react';
export function useRefreshOnFocus(refetch) {
useFocusEffect(
useCallback(() => {
refetch();
}, [])
);
}
useMutation
을 사용합니다. 아래는 게시글을 추가하는 예시입니다:import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';
const createPost = async (newPost) => {
const res = await axios.post('https://jsonplaceholder.typicode.com/posts', newPost);
return res.data;
};
export function NewPostButton() {
const queryClient = useQueryClient(); // 쿼리 클라이언트 인스턴스
const mutation = useMutation({
mutationFn: createPost, // 뮤테이션 함수
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['posts'] }); // 쿼리 데이터 무효화
},
});
return (
<Button
title="새 글 작성"
onPress={() => mutation.mutate({ title: '제목', body: '내용' })} // 뮤테이션 실행
/>
);
}
npx expo install @dev-plugins/react-query
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useReactQueryDevTools } from '@dev-plugins/react-query';
const queryClient = new QueryClient({});
export default function App() {
useReactQueryDevTools(queryClient); // 디버깅 도구 초기화
return (
<QueryClientProvider client={queryClient}>
{/* ... */}
</QueryClientProvider>
);
}
@dev-plugins/react-query
를 선택하면 웹 브라우저에서 디버깅 도구를 열 수 있습니다.