코딩 정보/React

[React] Query의 올바른 사용에 관해

꽁이꽁설꽁돌 2024. 11. 15. 19:21
728x90
반응형

목차

     

     

    먼저 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

     

    반응형