简体   繁体   English

在 React Native 中加载大量本地存储的 static 内容时,如何平滑过渡或显示加载器?

[英]How to make a smooth transition or display a loader while loading a lot of locally stored static content in React Native?

I have a React Native screen with ScrollView displaying countries with their phone country codes.我有一个带有 ScrollView 的 React Native 屏幕,显示国家及其电话国家代码。

const CountryCodes = () => {

import countries from "../../utils/countries";

 return (
    <ScrollView>
      {countries.map((country) => {
        return renderItem(country);
      })}
    </ScrollView>
  );
 };

the countries.js file is just an array of country data (c. 200 objects), together with require statements for the related assets, like this: countries.js 文件只是一个国家数据数组(大约 200 个对象),以及相关资产的 require 语句,如下所示:

const countries = [{
    letterCode: "ad",
    phoneCode: "376",
    name: "Andorra",
    flag: require("../assets/flags/ad.png"),
  }]

I am using the Stack Navigator from React Navigation.我正在使用 React Navigation 的 Stack Navigator。 The problem that I have is that when I am navigating to the CountryCodes screen, the screen is freezing (while, I assume, all the static flag images are loading) and after ~1 second it properly displays the ScrollView with all the countries.我遇到的问题是,当我导航到 CountryCodes 屏幕时,屏幕冻结(同时,我假设所有 static 标志图像正在加载),大约 1 秒后它正确显示所有国家的 ScrollView。

Is there a way to make the transition smoother?有没有办法让过渡更顺畅? I tried displaying a loader while the countries are loading, but the screen just keeps freezing with no chance to display anything in the meantime.我尝试在加载国家/地区时显示加载程序,但屏幕一直冻结,同时没有机会显示任何内容。 This issue is the same when using FlatList.这个问题在使用 FlatList 时也是一样的。

Please see the GIF below for reference.请参阅下面的 GIF 以供参考。

在此处输入图像描述

React native ScrollView doesn't do very well with a large number of entries. React native ScrollView不能很好地处理大量条目。 The recommendation is to use a Flatlist as it only renders the components that are currently visible in the viewport.You can see the usage here .建议使用Flatlist ,因为它只渲染当前在视口中可见的组件。您可以在此处查看用法。 If you need even faster performance, there's FlashList by shopify which is blazing fast.如果您需要更快的性能,可以使用shopify的 FlashList,它的速度非常快。

Example:例子:

<FlatList
    data={countries}
    renderItem={renderItem}
    keyExtractor={item => item.letterCode}
/>

You might need to change your renderItem to match the FlatList's renderItem signature.您可能需要更改renderItem以匹配 FlatList 的 renderItem 签名。

This lead me to a lot of frustration, but I managed to work out a sort of a hack to accomplish the goal with the use of setTimeout, like this:这让我很沮丧,但我设法想出了一种破解方法,通过使用 setTimeout 来实现目标,如下所示:

onPress={() => {
  setLoading(true);
  setTimeout(() => {
    navigation.navigate("Select Country")
  }, 0);
}}

When wrapped like this, the setLoading is executed first (and a lading screen appears) and only after after that the navigation.navigate is called and the Select Country screen appears after all the data is loaded.当像这样包装时,首先执行 setLoading(并出现一个加载屏幕),然后才调用 navigation.navigate 并在加载所有数据后出现选择国家/地区屏幕。

This is an issue while loading a heavy component.这是加载重组件时的问题。 this happen to me while loading a country list, multiple graph and other heavy components.在加载国家/地区列表、多个图表和其他繁重的组件时,这发生在我身上。

if you press on a navigation button then there is some delay while navigating to that screen or component.如果您按下导航按钮,那么在导航到该屏幕或组件时会有一些延迟。 I have write a custom hook for this.我为此编写了一个自定义挂钩。

paste the following code in your useIsReady.js将以下代码粘贴到您的useIsReady.js

import { useNavigation } from '@react-navigation/native'
import React from 'react'

const useIsReady = (stack = true) => {
  const navigation = useNavigation()
  const [isReady, setIsReady] = React.useState(false)
  React.useEffect(() => {
    const unsubscribeFocus = navigation.addListener('focus', () => {
      if (!isReady) setIsReady(true)
    })

    const unsubscribeTransitionEnd = stack
      ? // @ts-ignore
        navigation.addListener('transitionEnd', () => {
          if (!isReady) setIsReady(true)
        })
      : undefined

    return () => {
      unsubscribeFocus()
      unsubscribeTransitionEnd && unsubscribeTransitionEnd()
    }
  }, [])
  return isReady
}

export default useIsReady

here you can display a loading indicator on that component or screen Like this.在这里,您可以像这样在该组件或屏幕上显示加载指示器。

import useIsReady from '../../hooks/useIsReady';

  const DashBoardScreen = ({navigation}) => { 
      const isReady = useIsReady()
  if(!isReady){
                return(
                    <View style={{flex:1,justifyContent:"center", alignItems:"center",}} >
                        <ActivityIndicator  size={"large"} color="red" />
                    </View>
            )
        }

return  <YOUR_HEAVY_COMPONENT />
}

} }

or you can use lazy loading for multiple heavy components.或者您可以对多个重组件使用延迟加载。

Here is the example for lazy loading这是延迟加载的示例

import React, {memo, useMemo,Suspense, useEffect, useState } from 'react';
import {SafeAreaView,StatusBar,ActivityIndicator, Text,View,StyleSheet,TouchableOpacity, ScrollView } from 'react-native';
import { scale, verticalScale } from 'react-native-size-matters';
import { commonStyles,textStyles } from '../../styles';
import { colors, fonts } from '../../constants/theme';
import FontAwesome from 'react-native-vector-icons/FontAwesome'
import { hp, width, wp } from '../../constants/sacling';
import {sales_slider, recruiter_slider} from './sales_slider'
const BarChart  = React.lazy(() => import('./BarChart')) 
const PieChart = React.lazy(() => import('./PieChart'))
const HalfPieChart = React.lazy(() => import('./HalfPieChart'))


    const ItemView = memo(({item}) => {
        return(
            <TouchableOpacity 
                style={styles.ItemView}>
                <View style={{...styles.IconBgView, backgroundColor:item.bgColor}}>
                    {/* <AntDesign name={item.icon_name} size={scale(20)} color={"#fff"} /> */}
                    {item.icon}
                </View>
                <View style={{marginLeft:scale(5)}}>
                    <View style={{flexDirection:"row", justifyContent:"space-between"}}>
                        <Text style={styles.countText}>{item.count}</Text>
                        <View style={styles.ArrowPercentageRowView}>
                            <FontAwesome 
                                name={item.arrow_icon} 
                                color={item.arrow_icon_color} 
                                size={scale(8)}
                            />
                            <Text style={styles.ItemViewPercentage} >
                                {item.percentage}
                            </Text>
                        </View>
                    </View>
                    <View>
                        <Text style={styles.ItemViewTitle} >{item.title}</Text>
                    </View>
                </View>
            </TouchableOpacity>
        )
    })

    const CardView = memo(({title, items}) => {
        return(
            <View style={styles.CardView}>
                <Text style={styles.CardViewTitle}>{title}</Text>
                <ScrollView 
                    showsHorizontalScrollIndicator={false} 
                    showsVerticalScrollIndicator={false}
                    horizontal={true} >
                    {
                        items.map((item, index) => {
                            return(
                                <ItemView  
                                    key={`${index}`} 
                                    item={item}
                                />
                            )
                        })
                    }
                </ScrollView>
            </View>
        )
    })

    const LoadingView = () => {
        return(
            <View style={{flex:1,height:hp(20), justifyContent:"center", alignItems:"center",}} >
                <ActivityIndicator  size={"large"} color={colors.dark_primary_color} />
            </View>
        )
    }

    const DashboardAnaylyticsScreen = ({navigation}) => {
        const data = [
            { quarter:"Companies", earnings: .3 },
            { quarter:"Contacts", earnings: .6 },
            { quarter:"Meetings", earnings: .4 },
            { quarter:"Opportunities", earnings: .5 },
            { quarter:"Job Orders", earnings: .13 },
            { quarter:"Candidates", earnings: .16 },
            { quarter:"Interviews", earnings: .14 },
            { quarter:"Placements", earnings: .19 },
        ];
        const user_sales_actvites = [
            { quarter:"General Notes", earnings: .3 },
            { quarter:"Live Connect", earnings: .6 },
            { quarter:"Recieeved Email", earnings: .4 },
            { quarter:"Send Mails", earnings: .5 },
            { quarter:"Job Orders", earnings: .13 },
            { quarter:"Candidates", earnings: .16 },
            { quarter:"Interviews", earnings: .14 },
            { quarter:"Placements", earnings: .19 },
        ];
        const colors = ["tomato", "orange", "gold", "cyan", "navy", "pink", "gray","red" ]
        const RenderPieChart = useMemo(() => {
            return(
                <>
                    <Suspense fallback={<LoadingView />} >
                        <BarChart data={data} title={"General"} />
                    </Suspense>
                    <Suspense fallback={<LoadingView />} >
                        <PieChart data={data} colors={colors} />
                    </Suspense>
                    <Suspense fallback={<LoadingView />} >
                        <HalfPieChart data={data}  colors={colors} tilte={"Recruiter Performance"}/>
                    </Suspense>
                    {/* <Suspense fallback={<LoadingView />} >
                        <BarChart data={data} title={"Candidates in Stage"} />
                    </Suspense>
                     <Suspense fallback={<LoadingView />} >
                        <HalfPieChart data={data}  colors={colors} title={"Sales Performance"} />
                    </Suspense>
                  <Suspense fallback={<LoadingView />} >
                        <BarChart data={data} title={"Sales Activities"} />
                    </Suspense>
                      <Suspense fallback={<LoadingView />} >
                        <BarChart data={data} title={"Users Activities"} />
                    </Suspense> */}
                    {/*<Suspense fallback={<LoadingView />} >
                        <BarChart data={user_sales_actvites} title={"Sales Users Activities"} />
                    </Suspense> */}

                </>
            )
        },[data, colors])
        return (
            <SafeAreaView style={{flex:1, backgroundColor:colors.dark_primary_color}} >
                <StatusBar barStyle={"light-content"} />
                <View style={commonStyles.container} >
                    <ScrollView 
                        showsHorizontalScrollIndicator={false} 
                        showsVerticalScrollIndicator={false}
                        style={styles.container}>
                        <CardView 
                            title={sales_slider.title}
                            items={sales_slider.items}  
                        />
                         <CardView 
                            title={recruiter_slider.title}
                            items={recruiter_slider.items}  
                        />
                       {RenderPieChart}
                        <View style={{height:scale(100)}} />
                    </ScrollView>
                </View>
            </SafeAreaView>
            
        );
    };
    const areEqual = (prevProps, nextProps) => {
       
          return true
      }
export default memo(DashboardAnaylyticsScreen,areEqual);

const styles = StyleSheet.create({
    container:{
        width:width,
        backgroundColor:"#fff", 
        alignSelf:"center"
    },
    CardView:{
        width:wp(98),
        alignSelf:"center", 
        marginTop:scale(5), 
        borderWidth:1,   
        overflow:"hidden",
        borderRadius:scale(5),
        borderColor:"rgba(0,0,0,.1)"
    },
    CardViewTitle:{
        ...textStyles.Label,
        color:colors.text_primary_color,
        includeFontPadding:false,
        borderBottomWidth:1,
        fontFamily:fonts.Bold,
        borderColor:"rgba(0,0,0,.1)",
        padding:scale(5),
        width:wp(98)-2
    },
    ItemView:{
        flexDirection:"row",
        elevation:5,
       
        paddingHorizontal:scale(8),
        borderRadius:scale(5),
        borderRightColor:"rgba(0,0,0,.1)",
        alignItems:"center", 
        margin:scale(5), 
        backgroundColor:"#fff", 
        height:scale(40)
    },
    IconBgView:{
        width:scale(30),
        height:scale(30),
        justifyContent:"center",
        alignItems:"center", 
        backgroundColor:"#1C264A",  
        borderRadius:scale(25)
    },
    countText:{
        ...textStyles.Label,
        fontFamily:fonts.Bold,
        color:colors.text_primary_color,
        includeFontPadding:false,
        fontSize:scale(10),
    },
    ItemViewTitle:{
        ...textStyles.Label,
        color:colors.text_primary_color,
        includeFontPadding:false,
        fontSize:scale(10)
    },
    ItemViewPercentage:{
        ...textStyles.Label,
        color:colors.text_primary_color,
        includeFontPadding:false,
        marginLeft:scale(2),
        fontSize:scale(8)
    },
    ArrowPercentageRowView:{
        flexDirection:"row",
        flex:1,
        alignItems:"center", 
        marginLeft:scale(10), 
        justifyContent:"flex-start"
    }
})

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM