✨useQuery
https://tanstack.com/query/latest/docs/framework/react/reference/useQuery
1. 소개
useQuery
훅은 서버 데이터를 가져오기 위해 사용되는 훅입니다. 별도의 상태나 이펙트를 작성하지 않아도 됩니다. 또한 데이터 요청에 대한 로직들을 한 곳에 작성할 수 있기 때문에 코드를 깔끔하게 작성할 수 있습니다. queryKey와 queryFn을 필수로 입력해야 합니다.
2. useQuery
1
2
3
4
5
| const { data, error, status, ...returns } = useQuery({
queryKey,
queryFn,
...options
});
|
3. useQuery
옵션
#queryKey
- 필수
- 쿼리에 사용할 queryKey
- queryKey가 변경되면 쿼리가 자동으로 업데이트 됨
#queryFn
#enabled 옵션
- 쿼리를 자동으로 실행시킬지 여부 설정
false
로 설정하면 자동으로 실행되지 않음
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| const Todos = () => {
const { data, refetch } = useQuery({
queryKey: ["todos", { status, page }],
queryFn: fetchTodoList,
enabled: false
});
return (
<div>
{data.map((todo) => (
<Todo todo={todo} />
))}
</div>
);
};
|
#select 옵션
- 서버에서 가져온 데이터를 변환하거나 선택하는데 사용
- 반환된 데이터에는 영향을 주지만, 쿼리 캐시에 저장되는 내용에는 영향을 주지 않음
가져온 데이터 중에, 할일이 완료되지 않은 것만 선택
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| const Todos = () => {
const { data, refetch } = useQuery({
queryKey: ["todos", { status, page }],
queryFn: fetchTodoList,
select: (value: any) => value.filter((todo: Todo) => !todo.isDone)
});
return (
<div>
{data.map((todo) => (
<Todo todo={todo} />
))}
</div>
);
};
|
4. useQuery
반환값
#satus
- 쿼리의 상태
pending
: 캐시된 데이터가 없고 쿼리 시도가 아직 완료되지 않은 상태error
: 오류가 발생한 상태success
: 오류없이 응답을 성공적으로 수신한 상태
#isPending
- status.pending과 같은 의미
- 캐시가 없고, 쿼리 시도가 아직 완료되지 않아 오류중인 상태를 의미
#data
- 쿼리를 요청하고 받아온 응답 값
- 기본값은 undefined
#error
- 오류가 발생한 경우 반환되는 오류 객체
- 기본값은 null
#isLoading
- 첫번째 요청이 진행 중일 때의 상태
- isPending과 isFetching과 동일한 의미
#refetch
- 버튼을 클릭하는 것과 같은 액션이 발생할 때 데이터를 가져올 때
- 데이터가 업데이트되어 새롭게 데이터를 가져와야할 때 사용
1
2
3
4
5
6
7
8
9
10
11
| function Todos() {
const { data, refetch } = useQuery({
queryKey: ["todos", { status, page }],
queryFn: fetchTodoList
});
return (
<div>
<button onClick={() => refetch()}>refetch</button>
</div>
);
}
|
5. 같이 사용하면 좋은 옵션들
#enabled 옵션과 select옵션
초기에 todo 데이터를 가져오지 않고, 버튼을 클릭하면 refetch 함수를 통해 useQuery
를 실행하여 가져온 데이터 중에 할일이 완료되지 않은 것들만 선택하여 사용
- enabled를 false로 설정하여, 초기에 쿼리를 실행되지 않도록 설정
- select 옵션을 통해 할일이 완료되지 않은 것들만 선택
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| const { data, refetch } = useQuery({
queryKey: ["todos", { status, page }],
queryFn: fetchTodoList,
enabled: false, // 쿼리를 초기에 실행하지 않음
select: (value) => value.filter((todo) => !todo.isDone) // 가져온 데이터 중에 할일이 완료되지 않은 것만 선택
});
return (
<div>
{data.map((todo) => (
<Todo todo={todo} />
))}
<button onClick={() => refetch()}>다시 불러오기</button>
</div>
);
|
6. useState,useEffect를 사용했을 때 vs useQuery 사용했을 떄
useQuery
를 사용하면 useState
,useEffect
를 사용했을 때보다 적은 양의 코드를 작성하기 때문에 보다 깔끔한 코드를 작성할 수 있습니다.
다음은 params의 내용을 바탕으로 서버에 데이터를 요청하는 로직이 작성된 useBooks 훅입니다.
useState,useEffect를 사용했을 때
1. 상태와 이팩트 분리
- useState를 사용하여 상태를 관리
- useEffect를 사용하여 상태 변화에 따라 특정 동작을 수행
2. 수동 데이터 요청 및 업데이트
- 상태 변화에 따라 수동으로 데이터를 요청하고, setState를 사용하여 수동으로 업데이트 해야함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| import { IPagination } from "models/pagination.model";
export const useBooks = () => {
const { search } = useLocation();
const [books, setBooks] = useState<IBook[]>([]);
const [pagination, setPagination] = useState<IPagination>({
total_count: 0,
current_page: 1
});
useEffect(() => {
const params = new URLSearchParams(search);
const categoryIdParams = params.get(QUERYSTRING.CATEGORY_ID);
const newsParams = params.get(QUERYSTRING.NEWS);
const currentPageParams = params.get(QUERYSTRING.PAGE);
fetchBooks({
category_id: categoryIdParams ? Number(categoryIdParams) : undefined,
new: newsParams ? true : undefined,
page: currentPageParams ? Number(currentPageParams) : 1,
limit: LIMIT
}).then((res) => {
setBooks(res.lists);
setPagination(res.pagination);
});
}, [search]);
return { books, pagination };
};
|
useQuery 사용했을 때
1. 자동 데이터 관리
- useQuery는 데이터 요청과 업데이트를 자동으로 처리
- 쿼리의 상태 변화에 따라 자동으로 데이터를 요청하고, 데이터가 업데이트 되면 자동으로 컴포넌트를 렌더링 시킴
2. 데이터 요청 관련 상태 제공
- isLoading, isPending, isSuccess 등 데이터 요청 상태에 대한 값을 제공
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| export const useBooks = () => {
const { search } = useLocation();
const params = new URLSearchParams(search);
const { data: booksData, isLoading: isBooksLoading } = useQuery({
queryKey: ["books", search],
queryFn: () => {
const categoryIdParams = params.get(QUERYSTRING.CATEGORY_ID);
const newsParams = params.get(QUERYSTRING.NEWS);
const currentPageParams = params.get(QUERYSTRING.PAGE);
fetchBooks({
category_id: categoryIdParams ? Number(categoryIdParams) : undefined,
new: newsParams ? true : undefined,
page: currentPageParams ? Number(currentPageParams) : 1,
limit: LIMIT
});
}
});
return {
books: booksData?.lists,
pagination: booksData?.pagination,
isEmpty: booksData?.lists.length === 0,
isBooksLoading
};
};
|