为什么选中FlatList内的元素时,列表会自动滚动到顶部?
问题根因
你遇到的FlatList选中子项后自动滚动到顶部的问题,核心是触发了FlatList的全量重绘,无法保留原滚动位置,具体诱因在你的代码里有两处:
RenderBrand和BrandSeparator组件定义在FilterOptionBrand内部,每次选中子项修改tempFilterOptions触发组件重渲染时,这两个组件都会被重新创建,FlatList会判定renderItem和ItemSeparatorComponent属性发生变更,因此重新渲染所有列表项,重置滚动位置。- 未给FlatList配置
getItemLayout属性,动态数据更新时FlatList无法预计算每个item的位置,布局重计算后会默认回到初始滚动位置。
修复方案
- 把
RenderBrand、BrandSeparator组件移到FilterOptionBrand外部,需要用到的setBrandSelected等方法通过props传递,避免每次重渲染生成新组件 - 优化状态更新逻辑,用函数式更新代替深拷贝,避免状态依赖导致的不必要重渲染,同时性能更好。
参考调整后的代码:
// 移到FilterOptionBrand组件外部定义 const RenderBrand = ({ item, index, onPress }) => { return ( <TouchableOpacity onPress={() => onPress(index)}> {item.selected && ( <View style={[styles.brandImage, styles.selectedBrandImage]}> <Icon iconName={iconNames.Tick} /> </View> )} <FastImage source={{ uri: item.logo }} style={styles.brandImage} resizeMode={FastImage.resizeMode.contain} /> <Text style={styles.brandName}>{item.name}</Text> </TouchableOpacity> ); }; const BrandSeparator = () => <View style={styles.brandSeparator} />; const FilterOptionBrand = () => { const setBrandSelected = index => { // 改用函数式更新,避免依赖外部状态 setTempFilterOptions(prev => { const newOptions = [...prev]; const newEnums = [...newOptions[0].enums]; newEnums[index] = { ...newEnums[index], selected: !newEnums[index].selected }; newOptions[0] = { ...newOptions[0], enums: newEnums }; return newOptions; }); }; return ( <View style={styles.filterBrandMainContainer}> <View style={styles.searchBrandContainer}> <View style={styles.magnifyingGlassWrapper}> <Icon iconName={iconNames.MagnifyingGlass} /> </View> <TextInput style={styles.searchBrandInput} placeholder={searchBrandText} /> </View> <FlatList horizontal={true} showsHorizontalScrollIndicator={false} keyExtractor={(item, index) => String(item.id)} style={styles.collapsibleContainer} data={tempFilterOptions[0].enums} renderItem={({item, index}) => <RenderBrand item={item} index={index} onPress={setBrandSelected} />} ItemSeparatorComponent={BrandSeparator} // 新增getItemLayout配置,替换成你实际的item宽度和分隔符宽度 getItemLayout={(data, index) => { const itemWidth = 80; // 替换为你的品牌项实际总宽度 const separatorWidth = 12; // 替换为你的分隔符实际宽度 return { length: itemWidth + separatorWidth, offset: (itemWidth + separatorWidth) * index, index } }} /> </View> ); };
问题复现效果:
内容的提问来源于stack exchange,提问作者Erkan GÖRGÜLÜ
相关产品推荐
相关产品推荐

