ReactNative中SectionList重渲染卡顿优化及加载提示实现方法
优化方案
一、性能优化方案
- 修复内联函数导致的重渲染问题
现有代码里的renderItem、renderSectionHeader都是内联定义的,每次组件渲染都会生成新的函数引用,即使OrderItem用了React.memo,也会因为props中的函数引用变化触发重渲染。
优化方法:
- 把三个渲染相关函数提取到组件外部,或者用
useCallback包裹,保证函数引用稳定 - 不要在
renderItem里透传navigation,改用OrderItem内部调用useNavigation()获取导航实例,减少props传递的变量
示例修改:
// 组件外部定义或者用useCallback包裹 const keyExtractor = (item) => item.id; const renderSectionHeader = ({ section: { name } }) => ( <Text fontSize="sm" color="grey" py="2" px="4" bg="lightgrey"> {name} </Text> ); const renderItem = ({ item }) => <OrderItem order={item} />; // SectionList调用 <SectionList sections={internalOrders} keyExtractor={keyExtractor} renderItem={renderItem} renderSectionHeader={renderSectionHeader} removeClippedSubviews={true} maxToRenderPerBatch={5} windowSize={5} />
- 优化Redux Selector缓存
当前的筛选分组逻辑如果没有做缓存,每次redux状态变动都会重新计算分组,返回全新的sections数组引用,即使内容没有变化也会触发SectionList全量重渲染。
用reselect库创建带缓存的selector,只有当依赖的原始订单数据、筛选条件发生变化时,才重新计算分组结果,否则返回之前的缓存引用,避免不必要的重渲染。 - 完善
React.memo的比较逻辑React.memo默认只做浅比较,如果order属性每次都是新的对象引用,即使内容完全一致也会触发重渲染。给React.memo传入第二个自定义比较函数,只比较OrderItem实际用到的字段:
// OrderItem组件定义处 export default React.memo(OrderItem, (prevProps, nextProps) => { // 只比较实际渲染用到的字段,比如id、订单状态、金额等 return prevProps.order.id === nextProps.order.id && prevProps.order.status === nextProps.order.status && prevProps.order.amount === nextProps.order.amount; });
- SectionList自带属性优化
开启removeClippedSubviews={true}自动裁剪屏幕外的视图,调整maxToRenderPerBatch、initialNumToRender为刚好覆盖一屏的数量,windowSize设置为5左右(不要用默认的21,减少同时渲染的视图数量)。
二、加载Spinner实现方案
可以通过拆分渲染时序实现,避免用户感知卡顿:
- 在redux中新增一个
listLoading状态,触发筛选/分组操作前,先将listLoading设为true,此时页面优先渲染Spinner组件 - 用
InteractionManager.runAfterInteractions()包裹筛选分组的触发逻辑,等Spinner渲染完成后再执行耗时的计算和列表渲染逻辑,计算完成后再将listLoading设为false
示例代码:
// 触发筛选的逻辑 const handleFilter = useCallback((filterParams) => { dispatch(setListLoading(true)); InteractionManager.runAfterInteractions(() => { // 执行筛选分组的action,计算完成后自动更新sections数据 dispatch(filterOrders(filterParams)); dispatch(setListLoading(false)); }); }, [dispatch]); // 组件渲染处 return ( <View style={{flex: 1}}> {listLoading ? ( <ActivityIndicator style={{flex: 1}} size="large" /> ) : ( <SectionList {/* 原有配置 */} /> )} </View> )
如果卡顿问题还是存在,可以将筛选分组的逻辑放到独立的工作线程执行,避免阻塞JS主线程的UI渲染。
内容的提问来源于stack exchange,提问作者holyris
相关产品推荐
相关产品推荐

