나의 개발일지

[React] React query로 TodoList 리팩토링하기 본문

React

[React] React query로 TodoList 리팩토링하기

heew0n 2023. 12. 9. 04:11

< 설치해야 할 것들!  >

 

yarn add axois 

 

yarn add json-server

 

json-server --watch db.json --port 번호

 

yarn add react-query

 


 

 

src >  App.jsx

import React from "react";
import Router from "./shared/Router";
import { QueryClientProvider, QueryClient } from "react-query";

const queryClient = new QueryClient();

const App = () => {
  return (
    <QueryClientProvider client={queryClient}>
      <Router />;
    </QueryClientProvider>
  );
};

export default App;

 

QueryClientProvider : 데이터를 읽어오는 기능(QueryClient)을 애플리케이션 전체에 주입하도록 하는 API

 

 


 

 

 

< 조회 기능 구현 >

 

src > api > todos.js

// axios 요청이 들어가는 모든 모듈
import axios from "axios";

// 조회
const getTodos = async () => {
  const response = await axios.get(`${process.env.REACT_APP_SERVER_URL}/todos`);
  console.log("response", response.data);
  return response.data;
};

export { getTodos};

 

 

.env 파일 만들어서 localhost를 변수에 담아준다.

우리가 필요한 값은 response의 data !

export 잊지말자!

 

 

 

 

src > redux > components > TodoList.jsx

import { getTodos } from "../../../api/todos";
import { useQuery } from "react-query";


function TodoList({ isActive }) {
  // const todos = useSelector((state) => state.todos); // 리덕스 방식
  const { isLoading, isError, data } = useQuery("todos", getTodos);
  console.log("data", data);
  if (isLoading) {
    return <h1>로딩중입니다!!!!!!</h1>;
  }

  if (isError) {
    return <h1>오류가 발생했습니다!!!!</h1>;
  }
  
  // 코드 생략
  
   return (
   // todos 대신 가져온 data 넣기
   {data
          .filter((item) => item.isDone === !isActive)
          .map((item) => {
            return <Todo key={item.id} todo={item} isActive={isActive} />;
          })}
   
   )

 

 

const { isLoading, isError, data } = useQuery("todos", getTodos); 

구조분해할당으로 가져오기

useQuery의 key(todos)가 중요하다. 그 이유는 이따가 밑에서 설명하겠다

 

이 부분이 React Query가 가지고 있는 큰 장점이다.

Thunk를 쓸 땐, 초기값으로 isLoading, isError등을 직접 만들어야했다.

return 문에 도착하기 전에 isLoading 또는 isError에 따라 별도의 처리를 해주기 때문에 대기상태 처리 / 오류 처리에 대한 부분도 아주 쉽게 해결이 된다! 신세계를 경험 중..

 

 


 

 

< 추가 기능 구현 >

 

src > api > todos.js

// axios 요청이 들어가는 모든 모듈
import axios from "axios";

// 조회
const getTodos = async () => {
  const response = await axios.get(`${process.env.REACT_APP_SERVER_URL}/todos`);
  console.log("response", response.data);
  return response.data;
};

// 추가
const addTodo = async (newTodo) => {
  await axios.post(`${process.env.REACT_APP_SERVER_URL}/todos`, newTodo);
};

export { getTodos, addTodo };

 

 

src > redux > components >  Input.jsx

import { addTodo } from "../../../api/todos";
import { useMutation, useQueryClient } from "react-query";


function Input() {
 // const dispatch = useDispatch();

  // 리액트 쿼리 관련 코드
  const queryClient = useQueryClient();
  const mutation = useMutation(addTodo, {
    onSuccess: () => {
      queryClient.invalidateQueries("todos"); // useQuery key 가 중요! // todos를 무효화
      console.log("성공하였습니다!");
    },
  });
  
  
    // 추가하려는 todo를 newTodo라는 객체로 세로 만듦
    const newTodo = {
      title,
      contents,
      isDone: false,
      id: uuidv4(),
    };


    // dispatch(addTodo(newTodo)); // 원래 dispatch를 통해 실행시켰었음
    mutation.mutate(newTodo);
    // state 두 개를 초기화
    setTitle("");
    setContents("");
  };

 

 

Invalidate and refresh  이렇게 하면,

todos라는 이름으로 만들었던 query를  invalidate 할 수 있다

 

 

[invalidate의 과정]

Input.jsx에서 값 입력으로 인해 서버 데이터가 변경됨

→ onSuccess가 일어나면 기존의 Query인 “todos”는 무효화

→ 새로운 데이터를 가져와서 “todos”를 최신화시킴

→ TodoList.jsx를 갱신함

따라서 계속해서 리액트 앱은 최신 상태의 서버 데이터를 유지할 수 있게 되는 것

 

ex ) 만약 20:00에 todos의 데이터가 20개인데 3개가 더 추가되어서 21:00에 23개가 됐는데

       서버에만 저장되어있지만 렌더링하기 전까진 추가된 값이 보여지지 않는다. 그래서 invalidate를 쓰는 것이다

 

 

 


useQuery

import { useQuery } from 'react-query';
import { fetchTodoList } from '../api/fetchTodoList';

function App() {
	const info = useQuery('todos', fetchTodoList);
}

 

  • 첫 번째 인자 ‘todos’ --> 쿼리의 키(Query Keys)
  1. refetching 처리 ( invalidate )
  2. 캐싱(caching) 처리
  3. 애플리케이션 전체 맥락에서 이 쿼리를 공유하는 방법으로 쓰인다. 

                      —> 그 어느 컴포넌트 곳곳에 촥촥 뿌려져 있어도 같은 key면 같은 쿼리 및 데이터 보장!!

 

 

 

  • 두 번째 인자, ‘fetchTodoList’ --> 쿼리 함수(Query Functions) 비동기 함수
  1. 쿼리 함수는 promise 객체를 return 
  2. promise 객체는 반드시 data를 resolve하거나 에러 (resolve는 정상적으로 통신되었음을 의미 )

 

 

mutations 

  - CUD에서 사용된다

 

useMutation --> hook, 함수, API

mutation.mutate( 인자 )

 - 인자는 반드시 한 개의 변수 또는 객체여야 한다

 - mutation.mutate( 인자1, 인자2 ) --> 오류  

 - 결과는 객체형태이다

 - onSuccess일때는 꼭 Query key 사용!