form 태그와 useState를 사용해 입력을 받아서 검색어를 검색페이지로 useNavigate()를 사용해 검색 페이지로 보낸다. 그리고 반응형으로 브라우저 크기에 맞게 검색창 크기를 조절한다.
// src/Search/components/SearchBar.tsx
import { useState } from "react";
import { useNavigate } from "react-router-dom";
const SearchBar = (props: Props) => {
const navigate = useNavigate();
const [searchWord, setSearchWord] = useState<string>("");
const handleSearchSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
navigate(`/search/${searchWord}`);
setSearchWord("");
};
const handleChange = ({ target }: React.ChangeEvent<HTMLInputElement>) => {
const word = target.value;
setSearchWord(word);
};
return (
<form onSubmit={handleSearchSubmit}>
<SearchInput
value={searchWord ?? ""}
type="text"
onChange={handleChange}
/>
<SearchButton>검색</SearchButton>
</form>
);
};
export default SearchBar;
const SearchInput = styled.input`
border-radius: 40px;
margin-right: 1rem;
color: black;
padding: 5px;
@media screen and (max-width: 1023px) {
width: 100px;
}
@media screen and (max-width: 767px) {
width: 100px;
}
`;
const SearchButton = styled.button`
width: 50px;
height: 30px;
@media screen and (max-width: 1023px) {
width: 40px;
font-size: 5px;
}
@media screen and (max-width: 767px) {
width: 50px;
height: 30px;
}
`;
다른 영화 목록 페이지처럼 useInfiniteQuery와 InfiniteScroll를 사용해 무한 스크롤 페이지로 구성한다. 그리고 영화 상세 페이지에서처럼 useParams()를 사용해서 파라미터값을 가져온다.
// src/pages/Search/SearchMoviePage.tsx
const SearchMoviePage = (props: Props) => {
const { searchWord } = useParams() as { searchWord: string };
const { isLoading, isError, data, fetchNextPage, hasNextPage } =
useInfiniteQuery(
["search", searchWord],
({ pageParam = 1 }) => getSearchMovie(searchWord, pageParam),
{
getNextPageParam: (lastPage) => {
let page = lastPage.page;
if (lastPage.total_page === page) {
return false;
}
return page + 1;
},
}
);
if (isLoading) <h1>Loading...</h1>;
if (isError) <h1>Error ㅠㅠ</h1>;
return (
<InfiniteScroll loadMore={() => fetchNextPage} hasMore={hasNextPage}>
<MovieList>
{data?.pages?.map((page) => {
return page?.results.map((movie: MovieDetail) => (
<Fragment key={movie.id}>
<MovieCard movieData={movie} />
</Fragment>
));
})}
</MovieList>
</InfiniteScroll>
);
};
export default SearchMoviePage;
window.scrollY를 이용해 스크롤이 1000px 넘게 스크롤 됐을 경우 useState를 사용해 isScroll를 true로 변경해 스크롤업 버튼이 보이게 한다.
스크롤업 버튼을 누르면 window.scrollTo를 사용해 제일 위로 스크롤을 이동시킨다.
그리고 스크롤업 버튼의 z-index 값을 줘서 목록의 MovieCard와 겹칠경우 위쪽에 위치하게 한다.
// src/components/ScrollUpButton.tsx
import React, { useEffect, useState } from "react";
import styled from "styled-components";
type Props = {};
const ScrollUpButton = (props: Props) => {
const [isScroll, setIsScroll] = useState<boolean>(false);
const handleScroll = () => {
const { scrollY } = window;
scrollY > 1000 ? setIsScroll(true) : setIsScroll(false);
};
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);
const scrollToTop = () => {
window.scrollTo({ top: 0, behavior: "smooth" });
};
return isScroll ? (
<ScrollUpWrapper>
<button onClick={scrollToTop}>TOP</button>
</ScrollUpWrapper>
) : (
<></>
);
};
export default ScrollUpButton;
const ScrollUpWrapper = styled.div`
position: fixed;
right: 5%;
bottom: 5%;
z-index: 1;
button {
font-weight: bold;
font-size: 15px;
padding: 10px 10px;
border: 1px solid white;
border-radius: 35%;
background-color: #000;
color: #fff;
outline: none;
cursor: pointer;
}
`;