background

The pull-down refresh and pull-up loading function is a component that is used very frequently on the mobile terminal, whether it is an App or a small program. When developing RN recently, I thought that this wheel should be very mature, so I github all over 060b378959bd4e and couldn't find it. A refreshing component that feels very perfect, and many of the components with high star numbers were maintained a few years ago. When it comes to this, I have to complain. The ecology of RN is really too narrow in China, and it is a little bit niche. One question, the probability that a domestic search engine can find the answer is 50%, the probability of a certain song is 80%, and the repetition rate is very high. I have always thought that RN will be because react , the community should be very perfect, the result disappointed me .
After struggling for a while, I decided to use RN's own component- flatList , which was slightly encapsulated. There was no problem in using the Tab routing page, but when using the details page, I encountered the first load and switch tab. , The indicator did not show the bug when flatList was refreshed. I searched the entire network and only encountered it by foreign friends. I also saw the issue of rn, but it was about 18 years old. It was closed and there was no solution. The only I met someone who was exactly like me. His solution was to save the country by curve. Every time he refreshed, he made the list roll back to the top (because the indicator was not displayed, in fact, the list was rolled to the bottom to cover the chrysanthemum), which was unacceptable. This solution.

Development environment

RN version: 0.64.0
UI component library: react native elements
Component type: functional (hooks)
Navigation version (react native navigation): 5.X

Try the solution

No matter how you set the height of the list, or the height of the outer layer, the hard-coded height and the flex setting to 1 have all been tried, all of which are useless.

expected outcome

image-20210523231632386

actual results

image-20210523231658236

solution

After numerous times of debugging, I found that after removing the Header component of the react native elements component library, the chrysanthemum that entered the page for the first time was displayed normally, but the switching page still did not display. Because I want the list component to be displayed in a safe area, my flatList wraps a layer of SafeAreaView, and the style is set to flex: 1. When I tried to remove it, I replaced it with View, and now the result is displayed as I expected.

Parent component:

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

Subassembly:

<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>

Custom Header

After enabling the Header of react native elements, I started to look for an alternative Header solution, and finally decided to use the API provided by react native navigation.

screenOptions configures the default parameters of page navigation

Configure the global unified style of navigation:

<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 can accept a theme parameter, accept the theme style, and use useTheme to get the global style in the route.
<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;

So if some navigation needs some custom button events that need to be linked to the page, how to deal with it?

In fact, there is a setOptions method in navigation, which is the same function as when you configure Header Title, backTitle, etc. when configuring page routing.

// 设置自定义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 component

I encapsulated the general method, the method that needs to be implemented on the page is passed to the component through props, and I fixed the bug that may be encountered when the flatLIst component is pulled up and loaded.

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;

to sum up

RN stepped on the pit one step at a time, and the community couldn't give it, so we figured out how to solve it.


MangoGoing
785 声望1.2k 粉丝

开源项目:详见个人详情