surf-uiby COLDSURF
Native

PullToRefresh

당겨서 새로고침. 스크롤러를 감싸지 않고 props 를 돌려준다.

import { PullToRefresh } from '@coldsurfers/surf-ui/native/PullToRefresh'
import { useAnimatedRef } from 'react-native-reanimated'

function Feed() {
  const scrollableRef = useAnimatedRef<Animated.FlatList<Event>>()

  return (
    <PullToRefresh
      scrollableRef={scrollableRef}
      onRefresh={() => Promise.all([refetchFeed(), refetchBanner()])}
      onHapticFeedback={() => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)}
    >
      {(scrollableProps) => (
        <Animated.FlatList data={events} renderItem={renderEvent} {...scrollableProps} />
      )}
    </PullToRefresh>
  )
}

미리보기가 없는 이유

reanimated 의 워클릿 변환이 babel 플러그인인데, turbopack 은 node_modules 를 babel 대상에서 뺍니다. 이 사이트가 무는 워클릿은 DS 의 dist 안에 있어서 기본 배선이 닿지 않습니다 (#119).

뚫리더라도 제스처는 마우스로 흉내 내면 감각이 거짓이라, 이 표면은 실기기에서 보는 편이 맞습니다.

RN 기본 RefreshControl 을 왜 안 쓰나

그건 iOS UIRefreshControl · Android SwipeRefreshLayout 이 그리는 네이티브 뷰라 자식도 커스텀 인디케이터도 받지 않습니다. 색 채널(tintColor/colors)만 열려 있어서 색을 맞춰 봐야 모양은 플랫폼 것 그대로입니다. 그래서 당김 감지부터 이쪽이 합니다.

스크롤러를 감싸지 않고 돌려주는 이유

화면마다 스크롤러가 다릅니다 — 피드는 SectionList, 목록 화면은 FlatList 입니다. 어느 쪽을 쓸지는 소비처가 정하고, 여기서는 거기 꽂을 props 만 넘깁니다.

받은 props 는 그대로 펼쳐 넣어야 합니다.

props왜
ref안 달면 "지금 맨 위인가"를 알 수 없어 스크롤 중간에서도 당김이 걸립니다
bounces: falseiOS 네이티브 바운스가 켜져 있으면 네이티브가 밀어 올린 만큼 틈에 더해져 두 배로 움직입니다
overScrollMode: 'never'Android 글로우/스트레치도 같은 이유

scrollableRef 를 소비처가 만드는 이유

여기서 만들어 children 인자로만 주면, 소비처가 scroll-to-top 같은 다른 용도와 합치려고 렌더마다 새 콜백 ref 를 쓰게 됩니다. 그러면 React 가 매 렌더 ref(null) → ref(node) 를 돌려 당김 판정이 흔들립니다.

Android 에는 오버스크롤 바운스가 없다

스크롤 오프셋이 음수로 내려가지 않으므로 "오프셋이 마이너스면 당김" 이라는 iOS 식 판정이 통째로 안 먹습니다. 그래서 오프셋이 아니라 제스처의 이동량을 재고, 오프셋은 "지금 맨 위인가"를 가르는 데만 씁니다.

onRefresh 는 promise 를 돌려주면 된다

promise 가 끝날 때까지 인디케이터가 떠 있습니다. 화면이 여러 쿼리로 쪼개져 있으면 Promise.all 로 묶어 넘깁니다. 최소 노출 시간은 같이 기다립니다 — 순차로 기다리면 느린 응답에 그만큼이 그대로 얹힙니다.

실패는 삼킵니다. 네트워크가 죽으면 늘 나는 경로라 여기서 안 삼키면 unhandled rejection 이 됩니다. 인디케이터는 닫히고, 에러 표시는 소비처의 몫입니다.

onHapticFeedback 은 왜 콜백인가

DS 가 expo-haptics 를 물면 Expo 를 쓰지 않는 소비처까지 그걸 깔아야 합니다. 무엇을 어떤 세기로 울릴지도 화면의 맥락이라 소비처가 정합니다. 그래서 시점만 여기가 알고, 울리는 건 소비처가 합니다.

성공 · 실패를 가리지 않고 불립니다. "요청이 끝났다"는 신호지 "성공했다"는 신호가 아닙니다.

절대 위치 헤더가 있으면 topInset

헤더가 콘텐츠 위에 떠 있는 화면에서 0 에 열면 인디케이터 스트립이 헤더 뒤에 가립니다. 헤더 높이를 넘기면 그 아래에서 열립니다.

Props

Prop

Type

스크롤러에 펼쳐 넣는 props

Prop

Type

On this page