react-scroll-hooks을 이용해 scrollToTop 버튼 만들기

Maliethy·2021년 9월 14일
0

react

목록 보기
4/7
post-thumbnail

myInfo div structure

react-scroll-hooks를 사용하여 scrollTop Button을 구현했다.
참고로 예시로 든 코드는 antd, emotionjs, nextjs, react, typescript를 사용한 프로젝트이다.

내정보 페이지의 div 구조는 다음과 같다.

/pages/myInfo/index.tsx

...

import { useScroll } from 'react-scroll-hooks';
import BiArrowToTop from '@meronex/icons/bi/BiArrowToTop';
import useScrollFadeIn from '@hooks/useScrollFadeIn';

...

function MyInfo() {
  const [pageHeightInfo, setPageHeightInfo] = useState({ clientHeight: 0, scrollHeight: 0 });
  const [scrollToTopButtonOpacity, setScrollToTopButtonOpacity] = useState(0);
  const animatedItem = useScrollFadeIn(setScrollToTopButtonOpacity);
  const containerRef = useRef(null); // scroll할 영역
  const elementRef = useRef(null); // scroll될 대상
  const { scrollToElement, scrollToY } = useScroll({ scrollSpeed: 50, containerRef });
  
  ...
  
  
  useEffect(() => {
    setPageHeightInfo({
      clientHeight: document.documentElement.clientHeight,
      scrollHeight: document.documentElement.scrollHeight,
    });
  }, []);
  
  
  const scrollToTopButtonStyle = useMemo(
    () => ({ opacity: `${scrollToTopButtonOpacity}`, padding: '4.9px 10px' }),
    [scrollToTopButtonOpacity],
  );
  
  
  
 return (<>
      <AppLayout getListsDone={GetMyInfoDone}>
        <GlobalLayout />
        <MyInfoFormContainer ref={containerRef} style={{ position: 'relative', overflow: 'scroll' }}>
          <div ref={elementRef}>
           ...
            <MyInfoBodyContainer>
            
             ...
             
              <ScrollToTopTargetElement pageHeightInfo={pageHeightInfo} {...animatedItem} />
              <ScrollToTopButtonContainer>
                <Button
                  style={scrollToTopButtonStyle}
                  type="primary"
                  size="large"
                  icon={<BiArrowToTop />}
                  onClick={() => scrollToElement(elementRef, 100)}
                />
              </ScrollToTopButtonContainer>
            </MyInfoBodyContainer>
          </div>
        </MyInfoFormContainer>
      </AppLayout>
    </>)
    ...

scrollToTop Button이 보여지는 시기를 조절하기 위해 IntersectionObserver을 사용했다.
다음 코드는 브라우저 창 크기보다 30px 더 내렸을 때 scrollToTop Button이 fadeIn하도록 한다.

/layouts/PageLayout/index.tsx


interface ScrollToTopTargetElementProps {
  pageHeightInfo: { clientHeight: number; scrollHeight: number };
}

export const ScrollToTopTargetElement = styled.div(({ pageHeightInfo }: ScrollToTopTargetElementProps) => {
  return {
    position: 'absolute',
    top: pageHeightInfo.clientHeight + 30,
    right: 0,
    height: pageHeightInfo.scrollHeight,
  };
});

export const ScrollToTopButtonContainer = styled.div`
  position: fixed;
  bottom: 45px;
  right: 40px;
  transition: 0.5;
  transform: translate3d(0, 50%, 0);
`;

/hooks/useScrollFadeIn.tsx

import { useRef, useEffect, useCallback } from 'react';
const useScrollFadeIn = (setVisible) => {
  const dom = useRef(null);

  const handleScroll = useCallback(([entry]) => {
    if (entry.isIntersecting) {
      setVisible(1);
    } else {
      setVisible(0);
    }
  }, []);

  useEffect(() => {
    let observer;
    const current = dom.current;

    if (current) {
      observer = new IntersectionObserver(handleScroll, { threshold: 0.9 });
      observer.observe(current);
    }

    return () => observer && observer.disconnect();
  }, [handleScroll]);

  return {
    ref: dom,
  };
};

export default useScrollFadeIn;

결과물은 다음과 같다.

scrollToTop Button

참고 사이트
http://blog.hyeyoonjung.com/2019/01/09/intersectionobserver-tutorial/
https://codesandbox.io/examples/package/react-scroll
https://velog.io/@taeung/React-Custom-Hooks%EB%A1%9C-Scroll-Event-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0
https://github.com/jus0k/scroll-hooks

profile
바꿀 수 있는 것에 주목하자

0개의 댓글