Skip to content

Wrapping RefreshControl and passing it as JSX does not work on Android #49878

@coolsoftwaretyler

Description

@coolsoftwaretyler

Description

Hey folks, when using the refreshControl as a prop to ScrollView, I'm seeing a slight difference between Android and iOS.

Steps to reproduce

  1. If I directly use the RefreshControl component from React Native as JSX, it works on Android and iOS
  2. If I wrap RefreshControl in a custom component and pass it as JSX, it works on iOS, but Android crashes
  3. If I call my custom component as a function in the prop, it works on both Android and iOS.
import { ScrollView, RefreshControl } from 'react-native'

const CustomRefreshControl = () => {
  const [refreshing, setRefreshing] = useState(false);

  const onRefresh = () => {
    setRefreshing(true);
    setTimeout(() => {
      setRefreshing(false);
    }, 2000);
  };

  return <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />;
};

// This works on Android and iOS
const SomeScreen = () => {
      <ScrollView
        refreshControl={<RefreshControl refreshing={true} onRefresh={() => null} />}

// This works on iOS, not on Android
const SomeScreen = () => {
      <ScrollView
        refreshControl={<CustomRefreshControl />}

// This works on Android and iOS
const SomeScreen = () => {
      <ScrollView
        refreshControl={CustomRefreshControl()}

At first I thought maybe RefreshControl was special and didn't want to be wrapped. Or that the refreshControl prop on ScrollView always wanted to call a function. But the difference cross-platform makes me think that it's a bug somewhere.

Very minor pain point, but I thought I'd capture it in case that's helpful, or perhaps I've misunderstood how it ought to work.

React Native Version

0.78.0

Affected Platforms

Runtime - Android

Output of npx @react-native-community/cli info

System:
  OS: macOS 14.6
  CPU: (16) arm64 Apple M3 Max
  Memory: 224.30 MB / 48.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 20.18.0
    path: ~/.asdf/installs/nodejs/20.18.0/bin/node
  Yarn:
    version: 1.22.22
    path: ~/.asdf/installs/nodejs/20.18.0/bin/yarn
  npm:
    version: 11.1.0
    path: ~/.asdf/installs/nodejs/20.18.0/bin/npm
  Watchman:
    version: 2025.02.17.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.15.2
    path: /Users/tylerwilliams/.asdf/shims/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 24.0
      - iOS 18.0
      - macOS 15.0
      - tvOS 18.0
      - visionOS 2.0
      - watchOS 11.0
  Android SDK: Not Found
IDEs:
  Android Studio: 2024.2 AI-242.21829.142.2421.12409432
  Xcode:
    version: 16.0/16A242d
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.13
    path: /usr/bin/javac
  Ruby:
    version: 3.3.4
    path: /Users/tylerwilliams/.asdf/shims/ruby
npmPackages:
  "@react-native-community/cli":
    installed: 15.0.1
    wanted: 15.0.1
  react:
    installed: 19.0.0
    wanted: 19.0.0
  react-native:
    installed: 0.78.0
    wanted: 0.78.0
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: true
iOS:
  hermesEnabled: true
  newArchEnabled: true

Stacktrace or Logs

No errors come up in the debug tools from what I can see.

Reproducer

https://github.com/coolsoftwaretyler/RefreshControlReproducer

Screenshots and Videos

Video was too big for GH upload, here's a Dropbox: https://www.dropbox.com/scl/fi/a8106gdwf4st88lsemsuq/2025-03-06-refresher-bug.mov?rlkey=e61k2c5kh3l1ffh8h4k9wes51&st=hdw29mc4&dl=0

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions