Fetching 코드 작성
데이터를 담아 둘 상태 생성
useEffect 이용해 컴포넌트 Mount 시 데이터를 Fetching 한 뒤 상태 저장
const getServerData = async() => {
const data = await fetch ( "..." )
.then((res) => res.json());
return data;
};
export default function App() {
const [state, setState] = useState([]);
useEffect(() => {
getServerData()
.then((dataList) => setState(dataList))
.catch((e) => setState([]));
}, []);
...
}
useEffect : Side Effect 발생 → 흐름 파악 어렵게
export default function App() {
const { data } = useQuery(["data"], getServerData);
브라우저에 포커스가 들어온 경우
refetchOnWindowFocus
default: true
새로운 컴포넌트 마운트가 발생한 경우
refetchOnMount
default: true
네트워크 재연결이 발생한 경우
refetchOnReconnect
default: true
캐시 데이터의 유통기한 지정
staleTime
default값 : 0
→ 주로 애플리케이션의 데이터 새로고침 빈도 조절에 사용
저장한 데이터를 얼마나 유지할 지 지정
`gcTime` default값 : 5분
→ garbage collection time
→ 캐시된 데이터가 메모리에서 제거되기 전에 비활성 상태로 유지되는 시간
→ 메모리 사용량을 최적화하고 캐시에서 데이터를 언제 제거할 지 결정하는 데 사용
const { data } = useQuery(['data', getServerData, {
staletime: 10 * 60 * 1000;
cachetime: 10 * 60 * 1000;
})
→ 별다른 refresh가 없을 때, 10분 내 재호출시 API 호출X, 캐싱된 데이터 제공
enabled
옵션조건 충족될 때만 API 호출
const {data: data1} = useQuery(["data1"], getServerData);
const {data: data2} = useQuery(["data2", data1], getServerData, {
enabled: !!data1
});
React Query 설치
npm install @tanstack/react-query
React Query Client 설정
QueryClient
인스턴스를 생성QueryClientProvider를 사용하여 애플리케이션에 React Query 제공
QueryClient
인스턴스를 QueryClientProvider
를 사용하여 애플리케이션에 연결React Query의 Hooks 사용
import {QueryClient, QuertClientProvider} from 'react-query';
const queryClient = new QueryClient();
function App() {
return {
<QueryClientProvider client={queryClient}>
...
</QueryClientProvider>
};
}
useQuery
useMutation
queryKey
)queryKey
는 쿼리의 고유 식별자queryKey
는 문자열 또는 객체, 배열 등이 될 수 있음['post', postId]
와 같이 배열을 사용하여 queryKey
를 구성할 수 있습니다. 이렇게 배열을 사용하는 경우, 배열의 각 요소는 쿼리 키의 일부로 사용되어 쿼리의 고유성을 보장합니다.queryFn
)queryFn
은 비동기 데이터를 가져오는 함수queryFn
은 queryKey
를 매개변수로 받을 수 있으며, 이를 통해 데이터 요청 시 필요한 매개변수를 전달받을 수 있습니다. 예를 들어, queryKey
가 ['post', postId]
인 경우, queryFn
은 이 postId
를 사용하여 특정 포스트의 데이터를 요청할 수 있습니다.data
status = ‘pending’
이면 data
는 무조건 undefined
error
Error
객체isPending
: boolean, status
에서 파생된 값status == ‘pending’
)refetch
를 하는 경우에도)fetchStatus == 'paused'
일 경우에도 true라서isFetching
: boolean, fetchStatus
에서 파생된 값fetchStatus **==** ‘fetching’
)isLoading
: boolean, status & fetchStatus
에서 파생된 값isFetching && isPending
)isRefetching
: boolean, status & fetchStatus
에서 파생된 값isFetching && **!**isPending
)status
가 변할 때 마다 useQuery를 사용 중인 컴포넌트를 리렌더링 함처음 렌더링때 status = “pending”
(초기데이터 설정시 success)
도중에 에러 발생 시 리렌더링 및 status = “error”
데이터를 잘 받아왔으면 리렌더링 및 status = “success”
→ 추가적인 업데이트가 없다면 총 2번 리렌더링 됨
→ 추가적인 업데이트가 있을 때 마다 리렌더링
console.log
로 출력한 useQueryResult
객체의 몇 가지 속성들
값을 변경할 때 사용할 수 있는 API
const mutation = useMutation({
mutationFn: (newTodo) => {
return axios.post('/todos', newTodo)
},
})
return (
<div>
{mutation.isLoading ? (
'Adding todo ...'
) : (
<>
{mutation.isSuccess ? <div> Todo added! </div> : null}
<button onClick={() => {
mutation.mutate({id: new Date(), title: 'Write Diary' })
}}
>
Create Todo
</button>
</>
)}
</div>
)
optimistic update ✅
클라이언트 상태 관리
서버 요청이 완료되기 전에 클라이언트 측에서 데이터를 미리 업데이트하여 사용자에게 즉각적인 피드백을 제공하는 방법
데이터 수정 액션 → 서버에 요청 → 응답 대기 :(
서버 응답 오기 전에 클라이언트 측에서 데이터를 미리 업데이트
서버 요청 실패, 예상치 못한 응답 → 미리 업데이트한 상태를 롤백 필요 (추가적인 로직 필요)
이를 쉽게 구현할 수 있는 도구 제공
import { useMutation, useQueryClient } from 'react-query';
// 가정: 서버에 데이터를 업데이트하는 비동기 함수
const updateData = async (newData) => {
// API 요청을 통해 서버에 데이터 업데이트
// 여기서는 예시이므로 실제 API 호출 코드는 생략합니다.
};
const MyComponent = () => {
const queryClient = useQueryClient();
const { mutate } = useMutation(updateData, {
// Optimistic Update를 실행할 함수
onMutate: async (newData) => {
// 이전 상태를 캐싱해둡니다.
const previousData = queryClient.getQueryData('myData');
// 상태를 즉각 업데이트합니다.
queryClient.setQueryData('myData', old => ({ ...old, ...newData }));
// 롤백을 위해 이전 상태를 반환합니다.
return { previousData };
},
// Mutation이 성공했을 때
onSuccess: () => {
// 데이터가 성공적으로 업데이트되었으므로, 별도의 액션이 필요 없습니다.
},
// 에러 발생 시
onError: (err, newData, context) => {
// 에러가 발생하면 이전 상태로 롤백합니다.
queryClient.setQueryData('myData', context.previousData);
},
// Mutation이 완료(성공 또는 실패)된 후
onSettled: () => {
// 모든 상황에 대해 refetch를 실행하여 최신 상태를 유지합니다.
queryClient.invalidateQueries('myData');
},
});
const updateMyData = (data) => {
mutate(data);
};
return (
// UI 컴포넌트에서 updateMyData 함수를 사용하여 데이터 업데이트를 실행할 수 있습니다.
<button onClick={() => updateMyData({ key: 'value' })}>Update Data</button>
);
};
export default MyComponent;