react native flatList的菊花引起的血案

背景

下拉刷新上拉加載功能在移動端不管是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

預期結果

image-20210523231632386

實際結果

image-20210523231658236

開發環境

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>

自定義Header

啓用了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]);

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踩坑一步一個腳印,社區給不了的,本身想辦法解決。

相關文章
相關標籤/搜索