클릭한 메뉴의 위치로 스크롤이 이동하는 로직을 구현했다.
간단하지만 타입스크립트 사용, 사이드바와 별개의 컴포넌트인 바디, 헤더 높이 등 고려해야 할 여러 조건때문에 은근히 고민하게 만든 친구다.
1. useRef에 이동할 태그 저장하기
// mypage component
const navScrollListRef = useRef<any>([]);
return(
// ...중략
<div ref={el => { navScrollListRef.current[0] = el; }}>
<MypageTable title="마이페이지" />
</div>
<div ref={el => { navScrollListRef.current[1] = el; }}>
<MypageTable title="전체 주문 내역" />
</div>
<div ref={el => { navScrollListRef.current[2] = el; }}>
<AskTable title="1:1 문의 내역" />
</div>
// ...중략
)
일단 useRef를 사용하여 스크롤을 이동시킬 div의 위치를 저장한다. 사이드바의 메뉴 개수만큼 있어야 하므로 여러개를 하나의 useRef에 저장하기 위해 초기값을 배열로 준다.
return문 안에서 컴포넌트를 div로 감싸고 el(element)를 배열에 저장한다.
발생한 오류 1 :
const navScrollListRef = useRef<htmldivelement[]>([]);
여기서 처음에 타입을 <htmldivelement[]>로 주었는데 div element를 저장하는 곳에서 이런 에러가 났다.
tsconfig.json에 "strictNullChecks": true 설정이 되어있기 때문인데 설정을 꺼주거나(false) 타입을 강제로 할당하는 방법으로 해결할 수 있다 (https://stackoverflow.com/questions/48488701/type-null-is-not-assignable-to-type-htmlinputelement-reactjs). 나는 크게 타입 문제가 생길 부분이 아니라는 판단 하에 두 방법 모두 사용하지 않고 타입을 any로 주어 해결했다.
발생한 오류 2 :
<div ref={el => { navScrollListRef[0] = el; }}>
<MypageTable title="마이페이지" />
</div>
처음에 el을 navScrollListRef[0]에 바로 저장하려고 했더니 만난 에러다.
배열은 .current 안에 존재한다.
2. 사이드바 메뉴 클릭시 해당하는 인덱스 반환
// sidebar component
const handleScrollView = (e: any) => {
const menu = e.target.innerText;
const category: { [key: string]: number } = {
마이페이지: 0,
'전체 주문 내역': 1,
'1:1 문의 내역': 2,
'북마크 리스트': 3,
};
setNavScroll(category[menu]); //변수에 담기 (여기선 recoil 사용)
};
1. 사이드바 컴포넌트에서 클릭한 메뉴를 e.target.innerText로 가져와
2. category의 key와 일치한 value값을 찾아
3. 변수에 저장하는
함수를 만든다.
<div>
<div>
<span onClick={handleScrollView}>마이페이지</span>
</div>
<div>
<span onClick={handleScrollView}>전체 주문 내역</span>
<span onClick={handleScrollView}>1:1 문의 내역</span>
<span onClick={handleScrollView}>북마크 리스트</span>
</div>
</div>
함수는 각각의 메뉴에 적용한다.
발생한 오류 :
<div onClick={handleScrollView}>
<div>
<span>마이페이지</span>
</div>
<div>
<span>전체 주문 내역</span>
<span>1:1 문의 내역</span>
<span>북마크 리스트</span>
</div>
</div>
좀 바보같은 실순데 처음에 onClick을 전체를 감싸는 div에 줘서 에러가 났다.
이렇게 하면 스크롤 작동은 잘 하지만 사이드바의 빈 공간을 눌렀을 때 not found 페이지로 이동한다.
3. useEffect에 스크롤 이동 코드 작성
// mypage component
const navScrollIndex = useRecoilValue(NavScrollAtom);
useEffect(() => {
// sidebar scroll
if (navScrollListRef.current) {
navScrollListRef.current[navScrollIndex].scrollIntoView({
behavior: 'smooth',
});
}
})
난 상태관리 라이브러리로 리코일을 사용했기 때문에 2번에서 recoil atom에 저장한 값을 가져와 navScrollListRef.current의 인덱스 값으로 넣어준 후 .scrollIntoView를 이용하여 스크롤을 이동시켰다.
behavior을 smooth로 주면 스크롤이 부드럽게 이동한다.
4. +스크롤 위치 세부조정
이번엔 이동은 되는 것 같지만 스크롤이 애매한 위치에 가 있다.
왜 이러는건지 고민을 한참 하다가 window 전체 화면을 기준으로 스크롤이 이동한 것이 아닌가 하는 생각이 들었다.
즉 스크롤은 원하는 element로 잘 이동했지만 헤더를 침범한 것이다.
스크롤 위치 조정을 위해 검색을 열심히 하다가 스텍오버플로우에서 적절한 답변을 찾았다.
.example {
scroll-margin-top: 10px;
}
이렇게 스크롤에 margin을 넣을 수 있다고 한다 (https://stackoverflow.com/questions/24665602/scrollintoview-scrolls-just-too-far).
<div
style={{ scrollMarginTop: '240px' }}
ref={el => {
navScrollListRef.current[1] = el;
}}
>
<MypageTable title="전체 주문 내역" />
</div>
이런 식으로 적당한 margin 값을 찾아 적용했고 아래와 같이 잘 작동하는 모습을 볼 수 있었다.
참고자료
https://inthedev.tistory.com/27
https://stackoverflow.com/questions/24665602/scrollintoview-scrolls-just-too-far
'BookBuddy - Project > Function' 카테고리의 다른 글
[Book Buddy / Function] TypeScript 사용시 React Quill 이미지 사이즈 조절 (0) | 2023.09.05 |
---|---|
[Book Buddy / Function] input 자동 탭 기능 만들기 (0) | 2023.08.26 |
[Book Buddy / Function] 전화번호 숫자 길이 제한하기 (0) | 2023.08.24 |
[Book Buddy / Function] hover로 모달 사용하기 (0) | 2023.08.22 |