下拉刷新上拉加载功能在移动端不管是App仍是小程序都是很是高频使用的组件,最近在开发RN的时候,本想着这个轮子应该很成熟了,因而跑遍了github
都找不到一个自我感受很完美的刷新组件,并且star数高的组件不少都是几年前维护的,说到这个,不得不吐槽一下,RN的生态在国内真的是太窄了,遇到稍微小众一点的问题,国内某搜索引擎能找到答案的几率为50%,某歌的几率为80%,而且重复率很是高,一直觉得RN会由于react
的光环,社区应该会很是完善,结果令我失望。
挣扎了一番以后,决定仍是用RN自带的组件--flatList
,稍微封装了一层,在Tab路由页使用没有问题,可是在详情页使用的时候,遇到了第一次加载和切换tab时,flatList刷新时候的indicator没有显示的bug,找遍了全网,只有国外友人遇到过,rn的issue也看到过,可是是18年左右的,被人关了,而且没有解决方案,惟一一个遇到跟我如出一辙的人,他的解决方案是曲线救国,每次刷新的时候让列表滚回到顶部(由于indicator没有显示,实际上是列表滚到底部把那个菊花给覆盖了),不能接受这种解决方案。javascript
RN版本:0.64.0
UI组件库:react native elements
组件类型:函数式(hooks)
导航版本(react native navigaton):5.Xjava
无论怎么设置list的高度也好,外层高度也好,写死高度和flex设置为1全都试过,全都没有### 背景
下拉刷新上拉加载功能在移动端不管是App仍是小程序都是很是高频使用的组件,最近在开发RN的时候,本想着这个轮子应该很成熟了,因而跑遍了github
都找不到一个自我感受很完美的刷新组件,并且star数高的组件不少都是几年前维护的,说到这个,不得不吐槽一下,RN的生态在国内真的是太窄了,遇到稍微小众一点的问题,国内某搜索引擎能找到答案的几率为50%,某歌的几率为80%,而且重复率很是高,一直觉得RN会由于react
的光环,社区应该会很是完善,结果令我失望。
挣扎了一番以后,决定仍是用RN自带的组件--flatList
,稍微封装了一层,在Tab路由页使用没有问题,可是在详情页使用的时候,遇到了第一次加载和切换tab时,flatList刷新时候的indicator没有显示的bug,找遍了全网,只有国外友人遇到过,rn的issue也看到过,可是是18年左右的,被人关了,而且没有解决方案,惟一一个遇到跟我如出一辙的人,他的解决方案是曲线救国,每次刷新的时候让列表滚回到顶部(由于indicator没有显示,实际上是列表滚到底部把那个菊花给覆盖了),不能接受这种解决方案。react
RN版本:0.64.0
UI组件库:react native elements
组件类型:函数式(hooks)
导航版本(react native navigaton):5.Xandroid
无论怎么设置list的高度也好,外层高度也好,写死高度和flex设置为1全都试过,全都没有用,可是我在设置refreshing为true的地方加了定时器,延迟去刷新,就能正常显示菊花,因此我猜想是其余dom还没挂载渲染完,flatList就去刷新了,这个时候外层元素高度还没肯定,因此列表滚到最底下,形成了菊花消失的现象,可是useEffect会在组件渲染到屏幕以后执行,讲道理不该该会发生这种buggit
在无数遍的调试下,我发现把react native elements组件库的Header组件移除后,第一次进入页面的菊花正常显示了,可是切换页面仍是没有显示。因为我想让list组件在安全区域显示,因此个人flatList包裹了一层SafeAreaView,样式设置了flex:1,当我尝试把他移除后,使用View代替了它,如今结果如我预期显示。github
父组件:小程序
<View style={common.container}> <ButtonGroup onPress={updateIndex} selectedIndex={selectedIndex} buttons={['文件', '流程中心']} containerStyle={styles.buttonGroup} textStyle={styles.buttonGroupText} /> {selectedIndex === 0 ? <ProjectFileList /> : <ProjectWorkflow />} </View>
子组件:react-native
<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>
启用了react native elements的Header后,开始寻找替代的Header方案,最后仍是决定用react native navigation提供的api完成。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等等同样的功能。
// 设置自定义header useLayoutEffect(() => { 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]);
我把通用的方法封装了,须要在页面实现的方法经过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踩坑一步一个脚印,社区给不了的,本身想办法解决。