목차
먼저 react query를 들어가기 전에 useState의 남용 문제를 살펴보아야 한다.
useState의 남용
서버로부터 데이터를 불러오고(카테고리 리스트) 사용자가 카테고리를 필터링할 수 있도록 하는 코드
import { fetchData } from "./api";
import { computeCategories } from "./utils";
const App = () => {
const [data, setData] = React.useState(null);
const [categories, setCategories] = React.useState([]);
React.useEffect(() => {
async function fetch() {
const response = await fetchData();
setData(response.data);
}
fetch();
}, []);
React.useEffect(() => {
if (data) {
setCategories(computeCategories(data));
}
}, [data]);
return <>...</>;
};
사실 언뜻 보면 무엇이 잘못 되었지라는 생각이 든다.
이런 용도로 useEffect가 나온거 아닌가?
실제로 작동도 문제없이 잘된다. 하지만 다음과 같은 문제가 발생할 수 있다.
버튼 클릭 시 data의 변경
- 버튼 클릭으로 인해 data나 다른 상태가 변경되었다고 가정하면, 이로 인해 categories가 새로운 값으로 설정될 수 있다.
- categories 상태가 특정 조건에서만 업데이트되도록 보장되지 않았으므로 의도와 다르게 값이 변경될 가능성이 있다.
react-query의 데이터 재조회
- react-query를 사용하면 네트워크가 재연결되거나 사용자가 탭을 포커스할 때 데이터가 자동으로 재조회된.
- 재조회된 데이터가 data 상태를 업데이트하면, 이전과 동일한 로직에 의해 categories가 업데이트 된다.
- 사용자가 변경한 Y 값이 X 값으로 덮어쓰여질 수 있어 의도하지 않은 상태가 된다.
import { fetchData } from './api'
import { computeCategories } from './utils'
const App = () => {
const [data, setData] = React.useState(null)
- const [categories, setCategories] = React.useState([])
+ const categories = data ? computeCategories(data) : []
React.useEffect(() => {
async function fetch() {
const response = await fetchData()
setData(response.data)
}
fetch()
}, [])
- React.useEffect(() => {
- if (data) {
- setCategories(computeCategories(data))
- }
- }, [data])
return <>...</>
}
사실 위 내용은 useState보다는 useEffect에 대한 잘못된 이해로부터 시작되었다.
useEffect는 state를 react 외부의 무언가와 동기화시키기 위해서 사용되어야 하는데 이를 서로 다른 두 개의 react state와 동기화 시키는 데 이용하는 것은 옳지 않다.
react query의 남용
react-query로 데이터를 불러오고 localstate에 저장하는 코드
import React, { useState } from 'react';
import { useQuery } from 'react-query';
// 데이터 가져오는 함수
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
};
const DataComponent = () => {
// React Query로 데이터를 가져옴
const { data, error, isLoading } = useQuery('data', fetchData);
// 데이터를 상태에 저장
const [dataState, setDataState] = useState(null);
// 데이터가 로딩 중일 때
if (isLoading) return <div>Loading...</div>;
// 에러 처리
if (error) return <div>Error fetching data</div>;
// 데이터를 상태에 저장하는 함수
const handleSaveData = () => {
setDataState(data);
};
return (
<div>
<h1>Data Component</h1>
<button onClick={handleSaveData}>Save Data to State</button>
<div>
{dataState ? (
<pre>{JSON.stringify(dataState, null, 2)}</pre>
) : (
<p>No data saved yet.</p>
)}
</div>
</div>
);
};
export default DataComponent;
같은 맥락에서 react query에서 받아온 데이터는 state에 넣어서 사용해서는 안된다.
그이유는 다음과 같다.
useQuery로 받은 데이터를 로컬 상태에 넣는 경우, 이 로컬 상태는 React Query의 데이터 복사본이다.
즉, 로컬 상태에 데이터를 저장하는 순간, React Query에서 관리하는 데이터와 로컬 상태는 분리된다.
그 결과, React Query가 데이터를 업데이트할 때 그 변경 사항은 로컬 상태에는 자동으로 반영되지 않으므로 로컬 상태가 최신 상태를 유지하지 못한다.
즉 위의 예시처럼 서로 다른 두 개의 state를 동기화 시키는 것과 비슷한 사용이다.
결론
만약 queryCache (queryClient.setQueryData)를 조작한다면 낙관적인 업데이트 또는 mutation 이후 백엔드에서 받은 데이터를 작성하는 경우여야만 한다. 백그라운드에서 다시 불러오는 데이터는 현재 데이터를 덮어씌울 수 있다는 점을 기억해야 한다.
참고
https://highjoon-dev.vercel.app/blogs/1-practical-react-query
highjoon-dev
highjoon's dev-log
highjoon-dev.vercel.app
'코딩 정보 > React' 카테고리의 다른 글
[React][Vite] vite-plugin-svgr을 사용해 보자 (0) | 2024.12.06 |
---|---|
[React] vite 빌드 시 config에 대해 알아보고 경로 설정을 해보자 (0) | 2024.12.06 |
데이터 바인딩 이해하기 feat: (React, Vue.js) (1) | 2024.11.14 |
[React] useRef vs useState 중 무엇을 써야하지? (2) | 2024.11.12 |
[React][Virtual Dom][Fiber]에 대해 알아보자 (1) | 2024.09.16 |