-
Notifications
You must be signed in to change notification settings - Fork 24.9k
Description
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
- Install dependencies with
npm i
- Generate native code with
npx expo prebuild
- Run the application with
npx expo run:android
- Click on the button on the Home screen
- 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