背景

下拉刷新上拉加载性能在挪动端无论是App还是小程序都是十分高频应用的组件,最近在开发RN的时候,本想着这个轮子应该很成熟了,于是跑遍了github都找不到一个自我感觉很完满的刷新组件,而且star数高的组件很多都是几年前保护的,说到这个,不得不吐槽一下,RN的生态在国内真的是太窄了,遇到略微小众一点的问题,国内某搜索引擎能找到答案的概率为50%,某歌的概率为80%,并且反复率十分高,始终认为RN会因为react的光环,社区应该会十分欠缺,后果令我悲观。
挣扎了一番之后,决定还是用RN自带的组件--flatList,略微封装了一层,在Tab路由页应用没有问题,然而在详情页应用的时候,遇到了第一次加载和切换tab时,flatList刷新时候的indicator没有显示的bug,找遍了全网,只有国外友人遇到过,rn的issue也看到过,然而是18年左右的,被人关了,并且没有解决方案,惟一一个遇到跟我截然不同的人,他的解决方案是曲线救国,每次刷新的时候让列表滚回到顶部(因为indicator没有显示,其实是列表滚到底部把那个菊花给笼罩了),不能承受这种解决方案。

开发环境

RN版本:0.64.0
UI组件库:react native elements
组件类型:函数式(hooks)
导航版本(react native navigaton):5.X

尝试计划

不管怎么设置list的高度也好,外层高度也好,写死高度和flex设置为1全都试过,全都没有### 背景
下拉刷新上拉加载性能在挪动端无论是App还是小程序都是十分高频应用的组件,最近在开发RN的时候,本想着这个轮子应该很成熟了,于是跑遍了github都找不到一个自我感觉很完满的刷新组件,而且star数高的组件很多都是几年前保护的,说到这个,不得不吐槽一下,RN的生态在国内真的是太窄了,遇到略微小众一点的问题,国内某搜索引擎能找到答案的概率为50%,某歌的概率为80%,并且反复率十分高,始终认为RN会因为react的光环,社区应该会十分欠缺,后果令我悲观。
挣扎了一番之后,决定还是用RN自带的组件--flatList,略微封装了一层,在Tab路由页应用没有问题,然而在详情页应用的时候,遇到了第一次加载和切换tab时,flatList刷新时候的indicator没有显示的bug,找遍了全网,只有国外友人遇到过,rn的issue也看到过,然而是18年左右的,被人关了,并且没有解决方案,惟一一个遇到跟我截然不同的人,他的解决方案是曲线救国,每次刷新的时候让列表滚回到顶部(因为indicator没有显示,其实是列表滚到底部把那个菊花给笼罩了),不能承受这种解决方案。

预期后果

理论后果

开发环境

RN版本:0.64.0
UI组件库:react native elements
组件类型:函数式(hooks)
导航版本(react native navigaton):5.X

尝试计划

不管怎么设置list的高度也好,外层高度也好,写死高度和flex设置为1全都试过,全都没有用,然而我在设置refreshing为true的中央加了定时器,提早去刷新,就能失常显示菊花,所以我猜想是其余dom还没挂载渲染完,flatList就去刷新了,这个时候外层元素高度还没确定,所以列表滚到最底下,造成了菊花隐没的景象,然而useEffect会在组件渲染到屏幕之后执行,讲道理不应该会产生这种bug

解决方案

在无数遍的调试下,我发现把react native elements组件库的Header组件移除后,第一次进入页面的菊花失常显示了,然而切换页面还是没有显示。因为我想让list组件在平安区域显示,所以我的flatList包裹了一层SafeAreaView,款式设置了flex:1,当我尝试把他移除后,应用View代替了它,当初后果如我预期显示。

父组件:

<View style={common.container}>  <ButtonGroup    onPress={updateIndex}    selectedIndex={selectedIndex}    buttons={['文件', '流程核心']}    containerStyle={styles.buttonGroup}    textStyle={styles.buttonGroupText}   />  {selectedIndex === 0 ? <ProjectFileList /> : <ProjectWorkflow />}</View>

子组件:

<View style={styles.container}>      <SearchBar        containerStyle={styles.searchBarContainer}        inputStyle={styles.searchInput}        inputContainerStyle={styles.searchInputContainer}        lightTheme={true}        placeholder="搜寻"      />      <RefreshableList        loadMore={loadMore}        data={dataList}        renderItem={renderItem}        refreshing={refreshing}        onRefresh={handleRefresh}        onEndReached={handleLoadMore}        keyExtractor={item => item.resId}      /></View>

自定义Header

启用了react native elements的Header后,开始寻找代替的Header计划,最初还是决定用react native navigation提供的api实现。

screenOptions配置页面导航的默认参数

配置导航的全局对立款式:

<Stack.Navigator        initialRouteName="Login"        screenOptions={({ navigation }) => {          return {            headerStyle: {              backgroundColor: colors.primary            },            headerTitleStyle: {              color: '#FFFF'            },            headerBackTitleStyle: {              color: '#FFFF'            },            headerBackTitleVisible: false,            headerBackImage: () => (              <TouchableOpacity onPress={navigation.goBack}>                <Ionicons name="arrow-back" size={24} color={'#fff'} />              </TouchableOpacity>            ),            headerLeftContainerStyle: {              paddingLeft: 10            },            headerRightContainerStyle: {              paddingRight: 10            }          };        }} >  {// 页面的若干配置...}</Stack.Navigator>
NavigationContainer能够承受一个theme参数,承受主题款式,在路由外面就能应用useTheme拿到全局款式。
<NavigationContainer theme={MyTheme}>  {// 若干配置...}</NavigationContainer>

MyTheme.js

import { DefaultTheme } from '@react-navigation/native';const MyTheme = {  ...DefaultTheme,  colors: {    ...DefaultTheme.colors,    primary: '#0784ff',    bgGray: '#efeff3',    text: '#262626',    infoText: '#909399'  }};export default MyTheme;

那么如果有一些导航须要一些自定义的按钮事件须要跟页面联动,怎么解决呢?

其实,navigation中有setOptions办法,就是跟你在配置页面路由时配置Header Title、backTitle等等一样的性能。

// 设置自定义headeruseLayoutEffect(() => {  navigation.setOptions({    headerRight: () => (      <TouchableOpacity onPress={() => drawerRef.current.toggleSideMenu()}>        <Ionicons name="menu" size={24} color={'#fff'} />      </TouchableOpacity>    ),    headerTitle: () => (      <Text style={common.headerTitleText}>{route.params.name}</Text>    )  });}, [navigation]);

flatList组件

我把通用的办法封装了,须要在页面实现的办法通过props传递给组件 ,并且修复了flatLIst组件上拉加载可能遇到的bug。

import React, { useEffect, useState } from 'react';import { View, FlatList, ActivityIndicator, Text } from 'react-native';import styles from './styles';import Empty from '../Empty';const RefreshableList = props => {  const { setEndReachedCalled, loadMore } = props;  const renderFooter = () => {    return loadMore ? (      <View style={styles.footer}>        <ActivityIndicator />        <Text>正在加载更多数据...</Text>      </View>    ) : (      <></>    );  };  return (    <FlatList      {...props}      contentContainerStyle={props.data.length ? null : { flexGrow: 1 }}      onEndReachedThreshold={0.2}      onMomentumScrollBegin={() => {        // 有些页面遇到第一次加载就触发loadMore的状况,如首页我的项目核心,遇到此状况须要传递setEndReachedCalled函数管制触发条件        setEndReachedCalled ? setEndReachedCalled(false) : null;      }}      ListEmptyComponent={() => <Empty />}      ItemSeparatorComponent={        // eslint-disable-next-line no-undef        Platform.OS !== 'android' &&        (({ highlighted }) => (          <View style={[styles.separator, highlighted && { marginLeft: 0 }]} />        ))      }      ListFooterComponent={renderFooter}    />  );};export default RefreshableList;

总结

RN踩坑一步一个脚印,社区给不了的,本人想方法解决。