-
[React] PullToRefresh 구현 :: 마이구미React 2022. 6. 12. 00:06반응형
이 글은 PullToRefresh 에 대해 다룬다.
결과물의 코드는 ant.d 에서 제공하는 PullToRefresh 컴포넌트의 코드를 기반으로 작성되었다.
구현 방식뿐만 아니라 전반적인 PullToRefresh 기능 학습에 도움이 될 것이다.
참고 링크 - https://mobile.ant.design/components/pull-to-refresh
예제 코드 - https://codesandbox.io/s/pulltorefresh-b0mi3p?from-embedPullToRefresh 는 무엇인가?
PullToRefresh 기능은 아래로 끌어내리는 제스처를 통해 새로고침을 실행하게 해준다.
해당 제스처는 Swipe Down 또는 Pull 이라고 부를 수 있다.이 기능은 웹, 모바일 웹, 앱 구분없이 사용할 수 있다.
대부분 모바일 환경에서 많이 사용되는 기능이다.
모바일 웹에서는 대다수의 브라우저는 PullToRefresh 기능을 제공하고 있다.
이 글의 주제는 직접 구현하는 것인데 왜 구현하는지에 대해 의문이 생긴다.
이미 제공하고 있는데 왜 굳이 직접 구현하는가?
- 원하는 UI/UX 를 제공할 수 없다.
- 지원하지 않는 브라우저 환경이 존재한다.
- Android, IOS 에서 기본적으로 제공하는 이펙트가 서로 다르기에 일관성을 깨뜨린다.
PullToRefresh 를 직접 구현하게 되면 제시된 문제들을 해결할 수 있다.
어떻게 구현하는가?
PullToRefresh 구현에 앞서, 이 기능에 대해 정확한 이해가 필요하다.
- 발동 조건
- 상태
PullToRefresh 기능의 발동 조건은 어떻게 될까?
단순히 Swipe Down 제스처가 발생하는 경우라고 보면 안된다.
만약 스크롤이 존재한다면, 아래로 스크롤 이동이 된 상태가 아니여야한다.
(단순히 스크롤이 아래로 이동한 상태에서 Swipe Down 제스처를 하면 그냥 스크롤이 위로 올라가는 것이다.)
결론적으로 발동 조건은 scrollTop = 0 을 의미하는 것으로써, 스크롤 위치가 최상단에 있어야한다는 것을 의미한다.
PullToRefresh 의 상태는 어떻게 분류되는가?
제스처를 하다가 행동을 취소하고 싶을 수도 있고, 계속 제스처를 진행하고 있을 수도 있다.
결과적으로 상태는 4가지로 분류할 수 있다.
- pulling
- canRelease
- refreshing
- complete
실제 동작을 보면 더 이해하기 쉬울 것이다.
pulling 을 스와이프 제스처가 진행중을 의미한다.
canRelease 는 스와이프 이동 범위가 새로고침이 가능한 범위를 통과했다는 것을 의미한다.
refreshing 은 새로고침이 실행되어 진행중이라는 것을 의미한다.
complete 는 새로고침이 완료된 것을 의미한다.
"발동 조건", "상태" 이 2가지만 정확하게 이해하면 구현은 사실상 끝났다.
주의해야할 것은 발동 조건에서 언급했던 scrollTop = 0 이다.
스크롤 영역이 2개 이상으로 중첩되는 경우를 생각할 수 있다.
위 예제는 스크롤 영역이 2개 이상으로 중첩되는 경우로써, 전체 컨테이너 영역과 리스트 영역에 스크롤이 존재한다.
만약 리스트 영역에서 스크롤을 아래로 이동한 후에 Swipe Down 제스처를 하면 어떻게 될까?
단순히 리스트 영역의 스크롤이 다시 위로 이동하는 것을 예상할 수 있다.
하지만 컨테이너의 scrollTop 의 값만을 바라봤을 경우, PullToRefresh 기능까지 발동할 것이다.
컨테이너의 scrollTop 의 값이 0 이더라도 리스트 영역에서의 scrollTop 의 값이 0 이 아니면 PullToRefresh 는 발동하지 않아야한다.
결과적으로 현재 이벤트가 발생한 요소를 기준으로 scrollTop 의 값을 판단해야한다.
그래서 실제 예제 코드에서는 다음과 같이 scrollTop 의 기준이 되는 요소를 찾는 코드가 존재한다.
type ScrollElement = HTMLElement | Window; const defaultRoot = window; const overflowStylePatterns = ["scroll", "auto", "overlay"]; function isElement(node: Element) { const ELEMENT_NODE_TYPE = 1; return node.nodeType === ELEMENT_NODE_TYPE; } export function getScrollParent( el: Element, root: ScrollElement | null | undefined = defaultRoot ): Window | Element | null | undefined { let node = el; while (node && node !== root && isElement(node)) { if (node === document.body) { return root; } const { overflowY } = window.getComputedStyle(node); if ( overflowStylePatterns.includes(overflowY) && node.scrollHeight > node.clientHeight ) { return node; } node = node.parentNode as Element; } return root; }
혹시 이해가 안되었다면, 예제를 직접 동작해보면 이해할 수 있을 것이다.
(PC 도 테스트가 가능하지만, 모바일 모드로 전환해서 보는게 더 좋다.)
추가적으로
위에서 언급했듯이 PullToRefresh 는 대다수의 브라우저에서 기본적으로 제공하고 있다.
이것을 조금 생각해보면, 직접 구현한 PullToRefresh 와 브라우저의 PullToRefresh 간의 충돌에 대해 의문을 가질 수 있다.
그렇다면, 다음으로 이 글을 읽어보길 바란다.
반응형'React' 카테고리의 다른 글
[React] react-query 에러 핸들링 (2) 2024.09.29 [monorepo] hot reload 적용 :: 마이구미 (0) 2022.09.13 [React] Controlled vs Uncontrolled :: 마이구미 (2) 2022.06.01 [React] useImperativeHandle :: 마이구미 (0) 2022.04.27 [React] react-router v6 에서 Prompt 구현하기 :: 마이구미 (3) 2022.04.17