react / react/react-native

Horizontal ScrollView touchables stop responding when nested inside a sticky header (RN 0.79.2)

Đang mở
#51,290 20 bình luận 16 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

Component: ScrollView Needs: Triage :mag: Newer Patch Available
Ngôn ngữ chính
C++
Star
127k
Fork
25.3k
Merge trung bình
1 ngày 23 giờ
Pull request đã merge (30 ngày)
4

Mô tả

Description

When you nest a horizontal ScrollView with touchable tabs inside a vertical ScrollView (or SectionList) that uses stickyHeaderIndices, the horizontal scroll still works, but the individual tab TouchableOpacity buttons no longer fire their onPress once the header becomes pinned. As soon as you scroll back up and the header unsticks, the buttons work again.


Steps to reproduce
  1. Create a fresh RN 0.79.2 project (or use your existing app).
  2. Copy this component into App.js (or similar):
import React, { useState } from 'react'
import { View, Text, ScrollView, TouchableOpacity, StyleSheet, SafeAreaView } from 'react-native'

const ItemBox = ({ title }) => (
  <View style={styles.itemBox}>
    <Text>{title}</Text>
  </View>
)

export default function NestedScrollTest() {
  const tabs = [
    { key: 'one', title: 'Tab One' },
    { key: 'two', title: 'Tab Two' },
    { key: 'three', title: 'Tab Three' },
    { key: 'four', title: 'Tab Four' },
    { key: 'five', title: 'Tab Five' },
  ]

  // Generate lots of items so it scrolls
  const allItems = Array.from({ length: 50 }, (_, i) => `Item #${i + 1}`)

  const [activeTab, setActiveTab] = useState(tabs[0].key)

  const filteredItems = allItems.filter(
    (_, idx) => idx % tabs.length === tabs.findIndex(t => t.key === activeTab)
  )

  return (
    <SafeAreaView style={styles.container}>
      <TouchableOpacity
        style={styles.submitButton}
        onPress={() => alert(`Submitting on "${activeTab}"`)}
      >
        <Text style={styles.submitText}>Top Submit Button</Text>
      </TouchableOpacity>
      {/* Vertical scroll area with sticky header */}
      <ScrollView
        style={styles.scrollContainer}
        contentContainerStyle={{ paddingBottom: 20 }}
        nestedScrollEnabled
        removeClippedSubviews={false}
        stickyHeaderIndices={[0]}
      >
        {/* Sticky horizontal tab bar */}
        <View style={styles.stickyContainer} pointerEvents="box-none">
          <ScrollView
            horizontal
            showsHorizontalScrollIndicator={false}
            nestedScrollEnabled
            pointerEvents="auto"
            contentContainerStyle={styles.tabBar}
            onStartShouldSetResponder={() => true}
          >
            {tabs.map(tab => (
              <TouchableOpacity
                key={tab.key}
                onPress={() => setActiveTab(tab.key)}
                activeOpacity={0.7}
                style={[
                  styles.tabButton,
                  activeTab === tab.key && styles.activeTab,
                ]}
              >
                <Text style={
                  activeTab === tab.key ? styles.activeTabText : styles.tabText
                }>
                  {tab.title}
                </Text>
              </TouchableOpacity>
            ))}
          </ScrollView>
        </View>

        {filteredItems.map(title => (
          <ItemBox key={title} title={title} />
        ))}
      </ScrollView>

      <TouchableOpacity
        style={styles.submitButton}
        onPress={() => alert(`Submitting on "${activeTab}"`)}
      >
        <Text style={styles.submitText}>Bottom Submit Button</Text>
      </TouchableOpacity>
    </SafeAreaView>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  scrollContainer: {
    flex: 1,
  },
  stickyContainer: {
    backgroundColor: 'white',
    zIndex: 10,
    elevation: 4,
  },
  tabBar: {
    paddingVertical: 10,
    paddingHorizontal: 8,
  },
  tabButton: {
    paddingHorizontal: 16,
    paddingVertical: 8,
    marginRight: 8,
    borderRadius: 4,
    backgroundColor: '#EEE',
  },
  activeTab: {
    backgroundColor: '#4A90E2',
  },
  tabText: {
    fontSize: 14,
    color: '#333',
  },
  activeTabText: {
    fontSize: 14,
    color: '#FFF',
  },
  itemBox: {
    height: 80,
    marginHorizontal: 16,
    marginVertical: 8,
    backgroundColor: '#F9F9F9',
    borderRadius: 6,
    justifyContent: 'center',
    alignItems: 'center',
    elevation: 2,
  },
  submitButton: {
    padding: 16,
    backgroundColor: '#28A745',
    alignItems: 'center',
  },
  submitText: {
    color: '#FFF',
    fontSize: 16,
  },
})

  1. Run on Android.

  2. Scroll until the tab bar sticks to the top.

  3. Tap any tab button—nothing fires.

  4. Scroll up one pixel; tap again—the press now registers.

React Native Version

0.79.2

Affected Platforms

Runtime - Android

Output of npx @react-native-community/cli info
System:
  OS: Linux 6.8 Ubuntu 24.04.2 LTS 24.04.2 LTS (Noble Numbat)
  CPU: (6) x64 Intel(R) Core(TM) i5-9400F CPU @ 2.90GHz
  Memory: 12.45 GB / 23.38 GB
  Shell:
    version: 5.2.21
    path: /bin/bash
Binaries:
  Node:
    version: 22.11.0
    path: ~/.nvm/versions/node/v22.11.0/bin/node
  Yarn:
    version: 1.22.22
    path: ~/.nvm/versions/node/v22.11.0/bin/yarn
  npm:
    version: 10.9.0
    path: ~/.nvm/versions/node/v22.11.0/bin/npm

npmPackages:
  "@react-native-community/cli":
    installed: 18.0.0
    wanted: 18.0.0
  react:
    installed: 19.0.0
    wanted: 19.0.0
  react-native:
    installed: 0.79.2
    wanted: 0.79.2
MANDATORY Reproducer

https://github.com/Mootzali/NestedScrollIssue/

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Hướng nghiên cứu

Bắt đầu với reproducer bắt buộc và component App.js của nó trên Android, so sánh thao tác nhấn các tab khi header được ghim và khi không được ghim. Theo dõi cách xử lý thao tác chạm của React Native Android đối với ScrollView ngang lồng nhau và sticky header, sau đó xác minh rằng các control TouchableOpacity của tab vẫn phản hồi khi được ghim mà không làm gián đoạn việc cuộn.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
react-native
Lĩnh vực
mobile
Loại issue
Lỗi
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Ít trao đổi
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
48/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.