지난 팀프로젝트 1 기획글
에 따라 나는 select 컴포넌트, 무한 스크롤 페이지 구현 을 일단 진행했다.
이번에는 좀더 본질적으로 select의 기능을 이해하기 위해 ul ,li 태그를 이용해서 만들어 보기로 했다.
function SelectDropDown({ options }) {
const [selected, setSelected] = useState([]);
const [isOpen, setIsOpen] = useState(false);
const handleOptionClick = (option) => {
setSelected(option);
setIsOpen(false);
};
return (
<>
<DropDown>
<Selected isOpen={isOpen} onClick={() => setIsOpen(!isOpen)}>
{selected}
{isOpen ? (
<img src={arrowOpen} alt="^" />
) : (
<img src={arrowClose} alt="\/" />
)}
</Selected>
<Options isOpen={isOpen}>
{options.map((option, index) => (
<OptionList key={index} onClick={() => handleOptionClick(option)}>
{option}
</OptionList>
))}
</Options>
</DropDown>
</>
);
}
export default SelectDropDown;
....
export const DropDown = styled.div`
....
position: relative;
`;
export const Selected = styled.div`
/*isOpen 값에 따라 boder, color 등 스타일링 */
...
...
border: ${({ isOpen, theme }) =>
isOpen
? `2px solid ${theme.color.Grayscale500}`
: `1px solid ${theme.color.Grayscale300}`};
color: ${({ isOpen, theme }) =>
isOpen ? theme.color.Grayscale900 : theme.color.Grayscale500};
`;
export const Options = styled.ul`
display: ${({ isOpen }) => (isOpen ? "block" : "none")};
// => isopen 에따라 display 변경
.....
`;
export const OptionList = styled.li`
....
`;
일단 3번까지는 무리 없이 착착 진행되었다.
const handleLoad = async () => {
setIsLoading(true);
let limit = offset === 0 ? 8 : 9;
const { results, next } = await getMessage(limit, offset, id);
if (!results) return;
setMessages((prevMessages) => [...prevMessages, ...results]);
setOffset(offset + limit);
setIsLoading(false);
setHasNext(next);
};
function InfiniteScroll() {
const scrollRef = useRef(null);
// ...
return <div ref={scrollRef} />;
}

const { clientHeight, scrollHeight, scrollTop } = scrollRef.current;
콘솔 디버깅을 해본결과 나의 경우에는 이벤트의 등록과 삭제 모두 잘 되었지만, 스크롤이 끝까지 닿아도 event 핸들러 함수 내부로 들어가지 않았다.
function InfiniteScroll() {
const scrollRef = useRef(null);
// ...
return <div ref={scrollRef} style={{ overflowY: "auto" }} />;
}
스크롤 이벤트의 경우 스크롤을 내릴때마다 이벤트 등록과 삭제, 핸들러가 발생하고, 핸들러 내부에서 if 문으로 스크롤이 다 내려갔는지를 확인하는것이 반복되기에,
lodash 라이브러리를 이용해서 throttle을 사용하였다.
그리고 간혹 사용자중에 (나같은 경우가 그렇다.) 스크롤을 거칠게 내려서 사용환경에 따라 스크롤이 다시 튕겨 올라오는 경우를 대비해서
스크롤이 다내려갔는지 확인하는 if문에서 조건을
(clientHeight + scrollTop >= scrollHeight - 1) 라고 작성하여 스크롤이 다내려가기 1px 전에 이벤트가 발생하게끔하였다.
import { throttle } from "lodash";
...
const handleLoad = async () => {
setIsLoading(true);
let limit = offset === 0 ? 8 : 9;
const { results, next } = await getMessage(limit, offset, id);
if (!results) return;
setMessages((prevMessages) => [...prevMessages, ...results]);
setOffset(offset + limit);
setIsLoading(false);
setHasNext(next);
};
useEffect(() => {
if (!isLoading) {
handleLoad();
}
if (hasNext) {
console.log("이벤트 등록");
window.addEventListener("scroll", infiniteScroll);
}
return () => {
infiniteScroll.cancel();
window.removeEventListener("scroll", infiniteScroll);
console.log("이벤트 삭제");
};
}, [isScrollEnd]);
const infiniteScroll = useCallback(
throttle(() => {
if (!isLoading) {
const { clientHeight, scrollHeight, scrollTop } =
document.documentElement;
if (clientHeight + scrollTop >= scrollHeight - 1) {
setIsScrollEnd((prev) => !prev);
}
}
}, 200),
[]
);
const handelEditClick = () => {
setIsEdit(true);
};
const handelDeleteClick = () => {
setIsEdit(false);
};
//
return (
<div style={{ overflowY: "auto" }}>
<S.Contents>
{!isEdit && (
<Button style={{ marginBottom: "11px" }} onClick={handelEditClick}>
편집하기
</Button>
)}
{isEdit && <Button onClick={handelDeleteClick}>저장하기</Button>}
<Test3 isEdit={isEdit} messages={messages} />
</S.Contents>
</div>
);
}
export default Test;