Pull Fetcher

16094 단어 React후크
화면을 위로 가득 찰 때까지 스크롤하여 당기는 것으로 최신 데이터를 fetch 하는 앱으로 친숙한 기능입니다.
code: github/$ yarn 1214






usePullFetcher



이번 Custom Hooks 내역입니다.

components/usePullFetcher.ts
function usePullFetcher() {
  const [state, update] = useState<State>(...)
  const handleTouchDown = useCallback(...)
  const handleTouchUp = useCallback(...)
  const handleTouchMove = useCallback(...)
  useEffect(...)
  useEffect(...)
  return {
    state,
    handleTouchDown,
    handleTouchUp,
    handleTouchMove
  }
}

만진 순간 처리



터치 플래그를 켭니다.

components/usePullFetcher.ts
const handleTouchDown = useCallback(
  (event: TouchEvent<HTMLElement>) => {
    event.persist()
    update(_state => ({ ..._state, isTouching: true }))
  },
  []
)

손가락을 움직일 때의 처리



window.scrollY 가 0px 가 아닌 경우, early return 합니다. 「기세 잘 당겼다」판정을 하기 위해, 전회 동 이벤트 발생시의 좌표를 유지합니다. 임계값이 클수록 "기세"가 필요합니다.

components/usePullFetcher.ts
const handleTouchMove = useCallback(
  (event: TouchEvent<HTMLElement>) => {
    event.persist()
    update(_state => {
      if (!_state.isTop) return _state
      const y = event.touches[0].clientY - _state.startY
      const offsetY = y > 0 ? _state.threshold : 0
      const startY = event.touches[0].clientY
      return {
        ..._state,
        isTouching: true,
        startY,
        offsetY
      }
    })
  },
  []
)

떨어진 순간 처리



가볍게 만져 놓으면 처리가 달리지 않도록 차단합니다.

components/usePullFetcher.ts
const handleTouchUp = useCallback(
  (event: TouchEvent<HTMLElement>) => {
    event.persist()
    update(_state => {
      const offsetY =
        _state.offsetY > 0 ? _state.threshold : 0
      const fetched = offsetY === 0
      return {
        ..._state,
        fetched,
        isTouching: false,
        offsetY
      }
    })
  },
  []
)

useEffect



이번에는 서버를 세우지 않았기 때문에 간이적인 처리를 실시하고 있습니다만, async function 을 useEffect 로 즉시 실행하는 것으로 await 할 수가 있습니다.

components/usePullFetcher.ts
useEffect(() => {
  const handleScroll = () => {
    update(_state => {
      const isTop = window.scrollY === 0
      return { ..._state, isTop }
    })
  }
  handleScroll()
  window.addEventListener('scroll', handleScroll)
  return () =>
    window.removeEventListener('scroll', handleScroll)
}, [])
useEffect(
  () => {
    ;(async () => {
      if (state.fetched) return
      try {
        await wait(400)
        const newItems = getMailItems(1)
        const items = [
          ...state.items,
          ...newItems.map(item => ({
            ...item,
            date: new Date(item.date)
          }))
        ].sort(
          (a, b) => b.date.getTime() - a.date.getTime()
        )
        update(_state => ({
          ..._state,
          fetched: true,
          offsetY: 0,
          items
        }))
      } catch (error) {
        update(_state => ({
          ..._state,
          fetched: true,
          offsetY: 0,
          error
        }))
      }
    })()
  },
  [state.fetched]
)

render props



Custom Hooks를 이용하는 컴퍼넌트를 준비해, 아이 컴퍼넌트에 상태 변화를 전파시킬 때만 render props 합니다.

components/container.tsx
const View = (props: Props) => {
  const {
    state,
    handleTouchDown,
    handleTouchUp,
    handleTouchMove
  } = usePullFetcher()
  return (
    <div
      className={props.className}
      onTouchStart={handleTouchDown}
      onTouchEnd={handleTouchUp}
      onTouchCancel={handleTouchUp}
      onTouchMove={handleTouchMove}
    >
      {useMemo(() => props.render(state), [
        state.offsetY,
        state.fetched
      ])}
    </div>
  )
}

좋은 웹페이지 즐겨찾기