-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSelectBookStep.tsx
120 lines (103 loc) · 3.4 KB
/
SelectBookStep.tsx
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import { ComponentPropsWithoutRef, Suspense, useEffect, useState } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { useInView } from 'react-intersection-observer';
import type { MoveFunnelStepProps } from '@/components/common/Funnel';
import type { SelectBookStepFormValues } from '../../types';
import useBookSearchQuery from '@/queries/book/useBookSearchQuery';
import debounce from '@/utils/debounce';
import Input from '@/components/common/Input';
import Loading from '@/components/common/Loading';
import BookSearchList from '@/components/bookSearch/BookSearchList';
const SelectBookStep = ({ onNextStep }: MoveFunnelStepProps) => {
const { control, getValues, setValue } =
useFormContext<SelectBookStepFormValues>();
const keywordValue = getValues('queryKeyword');
const [keyword, setKeyword] = useState(keywordValue || '');
const debouncedSetKeyword = debounce(setKeyword, 500);
const handleEnterKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.code === 'Enter') e.preventDefault();
return;
};
return (
<article className="relative flex w-full flex-col gap-[1rem]">
<h2 className="font-subheading-bold">
어떤 책으로 독서모임을 시작할까요?
</h2>
<Input
type="search"
placeholder="책 제목, 작가를 검색해보세요"
inputStyle="line"
leftIconType="search"
className="mb-[1rem]"
defaultValue={keyword}
onChange={event => debouncedSetKeyword(event.target.value)}
onKeyDown={handleEnterKeyDown}
/>
<Suspense fallback={<Loading fullpage />}>
<Controller
name="book"
control={control}
render={({ field: { onChange } }) => (
<BookSearchResult
queryKeyword={keyword}
onBookClick={book => {
// update 'book' value in hook form
onChange(book);
// update 'queryKeyword' value in hook form
setValue('queryKeyword', keyword);
onNextStep && onNextStep();
}}
/>
)}
/>
</Suspense>
</article>
);
};
export default SelectBookStep;
// TODO: 도서 검색 페이지 컴포넌트와 공유할 수 있도록 분리
const BookSearchResult = ({
queryKeyword,
onBookClick,
}: {
queryKeyword: string;
onBookClick?: ComponentPropsWithoutRef<typeof BookSearchList>['onBookClick'];
}) => {
const { ref: inViewRef, inView } = useInView();
const bookSearchInfo = useBookSearchQuery({
query: queryKeyword,
page: 1,
pageSize: 12,
});
const searchedBooks = bookSearchInfo.isSuccess
? bookSearchInfo.data.pages.flatMap(page => page.searchBookResponseList)
: [];
const totalResultCount = bookSearchInfo.isSuccess
? bookSearchInfo.data.pages[0].totalCount
: 0;
useEffect(() => {
if (inView && bookSearchInfo.hasNextPage) {
bookSearchInfo.fetchNextPage();
}
}, [
bookSearchInfo.fetchNextPage,
inView,
bookSearchInfo.hasNextPage,
queryKeyword,
bookSearchInfo,
]);
// queryKeyword가 빈 값인 경우 검색되지 않으므로 null 반환
if (!queryKeyword) {
return null;
}
return (
<section>
<BookSearchList
books={searchedBooks}
totalCount={totalResultCount}
onBookClick={onBookClick}
/>
<div ref={inViewRef} />
</section>
);
};