Skip to content

newArchEnabled #53859

@c2d5c6

Description

@c2d5c6

Description

Why is there a problem with the new Android architecture? After tapping the second category, switching doesn't work anymore, blocking the transition. iOS doesn't have this problem.

`import React, { useRef, useState, useCallback, useMemo } from 'react';
import { View, ScrollView, FlatList, Text, TouchableOpacity, StyleSheet, Dimensions } from 'react-native';

const { height: screenHeight } = Dimensions.get('window');

const CATEGORIES = [
{ id: 1, title: 'Appetizers', color: '#FF6B6B' },
{ id: 2, title: 'Main Courses', color: '#4ECDC4' },
{ id: 3, title: 'Desserts', color: '#45B7D1' },
{ id: 4, title: 'Beverages', color: '#96CEB4' },
{ id: 5, title: 'Specials', color: '#FECA57' }
];

const ITEMS_PER_CATEGORY = 8;

export default function ScrollViewAnchorExample() {
const scrollViewRef = useRef(null);
const [selectedCategoryId, setSelectedCategoryId] = useState(1);
const [categoryPositions, setCategoryPositions] = useState<Map<number, number>>(new Map());
const [showTitleInHeader, setShowTitleInHeader] = useState(false);
const [headerPosition, setHeaderPosition] = useState(0);
const [isScrolling, setIsScrolling] = useState(false); // Добавляем флаг скролла
const lastScrollY = useRef(0); // Для throttling

// Скролл к якорю категории с проверками
const scrollToCategory = useCallback((categoryId: number) => {
    console.log('=== CATEGORY SELECT DEBUG ===');
    console.log('Selected category:', categoryId);
    console.log('Current selectedCategoryId:', selectedCategoryId);
    console.log('Timestamp:', Date.now());

    const categoryPosition = categoryPositions.get(categoryId);
    console.log('Category position for', categoryId, ':', categoryPosition);

    if (categoryPosition !== undefined && scrollViewRef.current) {
        const offsetY = Math.max(0, categoryPosition - 100);
        console.log('Scrolling to offsetY:', offsetY);

        // Сразу обновляем категорию без задержек
        setSelectedCategoryId(categoryId);

        // Простой скролл без флагов блокировки
        scrollViewRef.current.scrollTo({
            y: offsetY,
            animated: true
        });
    }
    console.log('================================');
}, [categoryPositions, selectedCategoryId]);

// Обработчик layout категории для сохранения позиции якоря
const handleCategoryLayout = useCallback((categoryId: number) => {
    return (event: any) => {
        const { y } = event.nativeEvent.layout;
        console.log(`📍 Category ${categoryId} layout at y:`, y);
        setCategoryPositions(prev => new Map(prev.set(categoryId, y)));
    };
}, []);

// Обработчик скролла с throttling
const handleScroll = useCallback((event: any) => {
    const scrollY = event.nativeEvent.contentOffset.y;

    // Throttling для уменьшения частоты обновлений
    if (Math.abs(scrollY - lastScrollY.current) < 5) return;
    lastScrollY.current = scrollY;

    // Показываем заголовок в header при скролле
    setShowTitleInHeader(scrollY > headerPosition + 50);

    // ОТКЛЮЧАЕМ автоматическое определение категории - это вызывает конфликты
    // Оставляем только ручное управление через нажатия
}, [headerPosition]);


const handleHeaderLayout = useCallback((event: any) => {
    const { y } = event.nativeEvent.layout;
    setHeaderPosition(y);
    console.log('📋 Header position set to:', y);
}, []);

// Обработчик завершения скролла
const handleMomentumScrollEnd = useCallback(() => {
    console.log('🏁 Scroll momentum ended');
}, []);

// Обработчик начала скролла пользователем
const handleScrollBeginDrag = useCallback(() => {
    console.log('👆 User started dragging scroll');
}, []);

// Мемоизированные элементы категорий для производительности
const categoryItems = useMemo(() => CATEGORIES, []);

return (
    <View style={styles.container}>
        {/* Основной заголовок */}
        <View style={styles.header}>
            <Text style={styles.headerTitle}>
                {showTitleInHeader ? 'Menu Categories' : 'Restaurant Menu'}
            </Text>
            {/* Добавляем индикатор состояния для отладки */}
            <Text style={styles.debugText}>
                Category: {selectedCategoryId} | Scrolling: {isScrolling ? 'YES' : 'NO'}
            </Text>
        </View>

        <ScrollView
            ref={scrollViewRef}
            onScroll={handleScroll}
            onMomentumScrollEnd={handleMomentumScrollEnd}
            onScrollBeginDrag={handleScrollBeginDrag}
            scrollEventThrottle={16}
            stickyHeaderIndices={[1]}
            showsVerticalScrollIndicator={true}
            // Добавляем дополнительные свойства для New Architecture
            nestedScrollEnabled={false}
            keyboardShouldPersistTaps="handled"
            removeClippedSubviews={false}
        >
            {/* Заголовочная секция */}
            <View onLayout={handleHeaderLayout} style={styles.heroSection}>
                <Text style={styles.heroTitle}>Welcome to Our Restaurant</Text>
                <Text style={styles.heroSubtitle}>Delicious food awaits you</Text>
            </View>

            {/* Sticky Header с категориями */}
            <View style={styles.stickyHeader}>
                <FlatList
                    data={categoryItems}
                    horizontal
                    showsHorizontalScrollIndicator={false}
                    keyExtractor={(item) => item.id.toString()}
                    contentContainerStyle={styles.categoriesContainer}
                    renderItem={({ item: category }) => (
                        <TouchableOpacity
                            style={[
                                styles.categoryChip,
                                selectedCategoryId === category.id && styles.categoryChipActive
                            ]}
                            onPress={() => {
                                console.log('🎯 TouchableOpacity pressed for category:', category.id);
                                scrollToCategory(category.id);
                            }}
                            activeOpacity={0.7}
                        >
                            <Text style={[
                                styles.categoryText,
                                selectedCategoryId === category.id && styles.categoryTextActive
                            ]}>
                                {category.title}
                            </Text>
                        </TouchableOpacity>
                    )}
                />
            </View>

            {/* Контент с категориями */}
            <View style={styles.contentContainer}>
                {categoryItems.map((category) => (
                    <View
                        key={category.id}
                        onLayout={handleCategoryLayout(category.id)}
                        style={styles.categorySection}
                    >
                        <Text style={[styles.categoryTitle, { color: category.color }]}>
                            {category.title}
                        </Text>

                        {/* Элементы в категории */}
                        {Array.from({ length: ITEMS_PER_CATEGORY }, (_, index) => (
                            <View key={index} style={[styles.menuItem, { borderLeftColor: category.color }]}>
                                <Text style={styles.itemTitle}>
                                    {category.title} Item {index + 1}
                                </Text>
                                <Text style={styles.itemDescription}>
                                    Description of the delicious {category.title.toLowerCase()} item
                                </Text>
                                <Text style={styles.itemPrice}>
                                    ${(Math.random() * 20 + 5).toFixed(2)}
                                </Text>
                            </View>
                        ))}
                    </View>
                ))}

                <View style={styles.bottomSpacing} />
            </View>
        </ScrollView>
    </View>
);

}

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#000',
},
header: {
backgroundColor: '#000',
paddingHorizontal: 16,
paddingVertical: 12,
borderBottomWidth: 1,
borderBottomColor: '#333',
},
headerTitle: {
color: 'white',
fontSize: 18,
fontWeight: 'bold',
},
// Добавляем стиль для отладочного текста
debugText: {
color: '#666',
fontSize: 12,
marginTop: 4,
},
heroSection: {
backgroundColor: '#1a1a1a',
padding: 32,
alignItems: 'center',
minHeight: 200,
justifyContent: 'center',
},
heroTitle: {
color: 'white',
fontSize: 28,
fontWeight: 'bold',
marginBottom: 8,
textAlign: 'center',
},
heroSubtitle: {
color: '#ccc',
fontSize: 16,
textAlign: 'center',
},
stickyHeader: {
backgroundColor: '#000',
paddingVertical: 12,
borderBottomWidth: 1,
borderBottomColor: '#333',
},
categoriesContainer: {
paddingHorizontal: 16,
},
categoryChip: {
backgroundColor: '#333',
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 20,
marginRight: 12,
},
categoryChipActive: {
backgroundColor: '#007AFF',
},
categoryText: {
color: '#ccc',
fontWeight: '500',
fontSize: 14,
},
categoryTextActive: {
color: 'white',
fontWeight: 'bold',
},
contentContainer: {
backgroundColor: '#000',
minHeight: screenHeight,
},
categorySection: {
paddingHorizontal: 16,
paddingTop: 24,
},
categoryTitle: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 16,
paddingBottom: 8,
borderBottomWidth: 2,
borderBottomColor: 'currentColor',
},
menuItem: {
backgroundColor: '#1a1a1a',
padding: 16,
marginBottom: 12,
borderRadius: 8,
borderLeftWidth: 4,
},
itemTitle: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
marginBottom: 4,
},
itemDescription: {
color: '#ccc',
fontSize: 14,
marginBottom: 8,
lineHeight: 20,
},
itemPrice: {
color: '#4CAF50',
fontSize: 16,
fontWeight: 'bold',
},
bottomSpacing: {
height: 100,
},
});`

Steps to reproduce

  1. Install dependencies with npm i
  2. Generate native code with npx expo prebuild
  3. Run the application with npx expo run:android
  4. Click on the button on the Home screen
  5. Notice the crash

React Native Version

0.81.4

Affected Platforms

Runtime - Android

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

System:
  OS: macOS 15.6
  CPU: (10) arm64 Apple M1 Pro
  Memory: 154.98 MB / 32.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 22.17.1
    path: /usr/local/bin/node
  Yarn: Not Found
  npm:
    version: 10.9.2
    path: /usr/local/bin/npm
  Watchman: Not Found
Managers:
  CocoaPods:
    version: 1.16.2
    path: /opt/homebrew/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 24.5
      - iOS 18.5
      - macOS 15.5
      - tvOS 18.5
      - visionOS 2.5
      - watchOS 11.5
  Android SDK: Not Found
IDEs:
  Android Studio: 2025.1 AI-251.26094.121.2512.13930704
  Xcode:
    version: 16.4/16F6
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.16
    path: /Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home/bin/javac
  Ruby:
    version: 2.6.10
    path: /usr/bin/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 19.1.0
    wanted: 19.1.0
  react-native:
    installed: 0.81.4
    wanted: 0.81.4
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: Not found
  newArchEnabled: Not found
iOS:
  hermesEnabled: Not found
  newArchEnabled: Not found

Stacktrace or Logs

No crash or error messages. The issue is behavioral:
- App builds and runs successfully
- No console errors

MANDATORY Reproducer

https://github.com/c2d5c6/React-Native.git

Screenshots and Videos

No response

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