6
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

React Nativeでスクロールの方向や量に応じて何かをするHook (TypeScript)

Last updated at Posted at 2020-04-10

まえがき

ライブラリ作るほどでもないけど毎回コード書くのも面倒なので、リファレンス的な目的でcustom hookをべたっと貼りました。ScrollViewをラップしてRenderPropにしちゃってもOK。

用途は

  • 下に一定スクロールしたら消え、上にスクロールすると戻るヘッダ(exampleはこれ)
  • 下に一定スクロールするとXXする何か
  • 一番下にスクロールしたら追加ロード
  • などなど

結論

こんな感じで便利Custom hookを作って、、、

import { useRef, useCallback } from 'react'
import { NativeSyntheticEvent, NativeScrollEvent } from 'react-native'

export function useScroll({
  onScrollUp,
  onScrollDown,
  onScrollRight,
  onScrollLeft,
  onScrollToBottom,
}: {
  onScrollUp?: (offsetY: number) => void
  onScrollDown?: (offsetY: number) => void
  onScrollRight?: (offsetX: number) => void
  onScrollLeft?: (offsetX: number) => void
  onScrollToBottom?: () => void
}) {
  const offset = useRef({ x: 0, y: 0 })

  const onScroll = useCallback(
    (event: NativeSyntheticEvent<NativeScrollEvent>) => {
      const { nativeEvent } = event
      const diffY = nativeEvent.contentOffset.y - offset.current.y
      const diffX = nativeEvent.contentOffset.x - offset.current.x

      if (onScrollDown && diffY > 0) {
        onScrollDown(diffY)
      }

      if (onScrollUp && diffY < 0) {
        onScrollUp(diffY)
      }

      if (onScrollRight && diffX > 0) {
        onScrollRight(diffX)
      }

      if (onScrollLeft && diffX < 0) {
        onScrollLeft(diffX)
      }

      if (
        onScrollToBottom &&
        nativeEvent.layoutMeasurement.height + nativeEvent.contentOffset.y >=
          nativeEvent.contentSize.height
      ) {
        onScrollToBottom()
      }
    },
    [onScrollDown, onScrollLeft, onScrollRight, onScrollToBottom, onScrollUp]
  )

  const onScrollBeginDrag = useCallback(
    (event: NativeSyntheticEvent<NativeScrollEvent>) => {
      const { nativeEvent } = event
      offset.current = nativeEvent.contentOffset
    },
    []
  )

  return {
    onScroll,
    onScrollBeginDrag,
  }
}

あとはScrollViewを使ってる画面で使うだけ。


const THREASHOLD = 50

function SampleApp() {
  const [isHeaderActive, toggleHeader] = useState(true)
  const { onScroll, onScrollBeginDrag } = useScroll({
    onScrollDown: offsetY => {
      if (offsetY >= THREASHOLD) {
        toggleHeader(false)
      }
    },
    onScrollUp: () => {
      toggleHeader(true)
     },
  })
    
  return (
    <ScrollView
      onScroll={onScroll}
      onScrollBeginDrag={onScrollBeginDrag}
    >
      { isHeaderActive && <HeaderComponent /> }
    </ScrollView>
  )
}

6
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?