Skip to content

newArchEnabled #53859

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

    Labels

    Needs: Author FeedbackNeeds: ReproThis issue could be improved with a clear list of steps to reproduce the issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions