在React Native Expo应用中加载内容的神秘随机延迟

发布于 2025-01-29 18:08:03 字数 8885 浏览 2 评论 0原文

在启动屏幕和动画介绍之后(请参见下面的代码)之后,大多数时间内容都会立即出现,但有时随机占10-15s 。出现后,任何随后的访问都将正常工作,至少有一段时间。我无法弄清楚它是应用程序内错误还是服务器/数据库问题。

这是一个gif说明流程正常工作时的流程:

“在此处输入图像说明”

请注意类别列表如何在“查看此删除”介绍图image之后出现。 ,介绍映像逐渐消失,但是类别不会出现15s。

使用:

- Expo Go on iOS (only happens with expo publish, not in dev mode)

- Apollo and GraphQL to fetch content hosted on Heroku Postgres database.

- Heroku free dyno and Hobby-dev Postgres addon db (could this be the reason?)
  • 如果我在此延迟进行时最小化并返回应用程序 - Apollo Usequery usequery Hook将返回错误。

  • 如果我不尽量减少并且只等待 - 没有错误,最终会加载。

  • 它没有显示加载栏 - 这应该意味着它不会粘在“加载”状态。标题和页脚可见 - 只有空屏幕应该在其中。

homescreen.js

import React, { useEffect, useState } from 'react'
import { View, Text, FlatList, Pressable, TouchableOpacity, ImageBackground, Image, Animated } from 'react-native'
import { useFocusEffect } from '@react-navigation/native';
import { gql, useQuery } from '@apollo/client'
import Bg from "../../../assets/background.png"
import Intro from "../../../assets/intro2x.png"
import { useFavContext } from '../../components/FavContext';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { StatusBar } from 'expo-status-bar';
import { LoadingLarge } from '../../components/Loading';

const HOME = gql`
  query HomeData {
    home {
      data {
        attributes {
          ...
        }
      }
    } 
  }
`  
const IntroMessageAnimation = (props) => {
    const fadeAnimat = React.useRef(new Animated.Value(0)).current; // Initial value for opacity: 0
    useFocusEffect(
        React.useCallback(() => {
            setTimeout(() => {
            Animated.timing(fadeAnimat, {
                toValue: 1,
                duration: 500,
                useNativeDriver: true,
            }).start();
            }, 500);
            setTimeout(() => {
                Animated.timing(fadeAnimat, {
                    toValue: 0,
                    duration: 500,
                    useNativeDriver: true,
                }).start();
            }, 2000);
        },[]))
    return (
        <Animated.View // Special animatable View
            key="1ab"
            style={{
            flex: 1,
            opacity: fadeAnimat, // Bind opacity to animated value
            justifyContent: 'center',
            alignItems: 'center'
            }}>
            {props.children}
        </Animated.View>
    );
}

const IntroMessage = () => (
    <IntroMessageAnimation >
        <Image
            style={{ 
                width: 273, 
                height: 324, 
            }}
            source={Intro}
            defaultSource={Intro}
            resizeMethod='scale'
            resizeMode='contain'
        />
    </IntroMessageAnimation >
)

const MainContentAnimation = (props) => {
    const fadeMainAnimat = React.useRef(new Animated.Value(0)).current; // Initial value for opacity: 0
    useEffect(() => { 
        const timeout = setInterval(() => {
            Animated.timing(fadeMainAnimat, {
                toValue: 1,
                duration: 500,
                useNativeDriver: true,
                }).start();
        }, 500);
        return () => clearInterval(timeout)
    })
    
    return (
      <Animated.View // Special animatable View
        style={{
          flex: 1,
          opacity: fadeMainAnimat, // Bind opacity to animated value
        }}>
        {props.children}
      </Animated.View>
    );
}

export default ({ navigation }) => {
    const insets = useSafeAreaInsets();
    const { favorites } = useFavContext()
    const { data, loading, error } = useQuery(HOME, {
        notifyOnNetworkStatusChange: true,
        fetchPolicy: 'network-only', // Used for first execution
        nextFetchPolicy: 'cache-first', // Used for subsequent executions
    })
    const [isReady, setIsReady] = useState(false)

    useEffect(() => {
        const timer = setTimeout(() => {
            setIsReady(true);
        }, 2700);
        return () => clearTimeout(timer);
    }, []);
      
    if ( !isReady || loading ) {
        return (
            <ImageBackground source={Bg} resizeMode="cover" style={tw.style('flex-1 relative')}>
                <StatusBar style='light'/>
                <IntroMessage/>
            </ImageBackground>
        )
    } else if ( loading ) {
        return <LoadingLarge />
    } 

    if (error) {
        return <Text>Error :( </Text>;
    }

    const heading = data.home.data.attributes.Heading;
    const subheading = data.home.data.attributes.Subheading;
    const categories = data.home.data.attributes.categories

    const MainScreen = () => (
        <MainContentAnimation >
            <View
                style={{
                   // ... inset paddings ...
                }}>
                <StatusBar style='light'/>
                <View>
                    <Text>{heading}</Text>
                    <Text>{subheading}</Text>
                </View>  
                <View>
                    <FlatList
                        showsVerticalScrollIndicator={false}
                        data={categories.data}
                        renderItem={({ item }) => (
                            <CategoryItem
                                favorites={favorites}
                                navigation={navigation}
                                category={item}
                                onPress={() => 
                                navigation.navigate('CategoryScreen', { item: item })}
                            />
                        )}
                        keyExtractor={(item) => item.id.toString()}
                    /> 
                </View>
            </View>
        </MainContentAnimation >
    )
    
    return (
        <ImageBackground source={Bg} resizeMode="cover" style={tw.style('flex-1 relative')}>
            <MainScreen/>
        </ImageBackground>
    )
}

如果相关, app.js

// Initialize Apollo Client
const client = new ApolloClient({
  uri: 'https://janazaapp.herokuapp.com/graphql/',
  cache: new InMemoryCache({
    typePolicies: {
      Subsubcategory: {
        merge: true
      },
    },
  })
});

function HomeStackScreen() {
  return (
    <HomeStack.Navigator 
      initialRouteName="HomeScreen"
      screenOptions={({ navigation }) => {
        return {
          detachPreviousScreen: !navigation.isFocused(),
          headerShown: true, 
          headerMode: 'float',
        }
      }}
    >
      <HomeStack.Screen 
        name="HomeScreen" 
        component={FadeHomeScreen} 
        options={({ navigation }) => ({
          headerTitle: ({props}) => <LogoTitle onPress={() => navigation.navigate('HomeScreen')} />,
        })}
      />
      // ... other screrens ...
  </HomeStack.Navigator>
  );
}


  function MainScreen() {
    const ref = React.useRef(null);
    const newFavor = JSON.stringify(favor.current)

    return (
      <SafeAreaProvider>
      <ApolloProvider client={client}>
          <NavigationContainer theme={MyTheme} ref={ref}>
            <FavProvider favor={newFavor}>
              <Tab.Navigator
                screenOptions={({ route, navigation }) => ({
                    // ... screen options ...
                })}
              >
                <Tab.Screen 
                  name="Home" 
                  component={HomeStackScreen} 
                  options={{ tabBarLabel: 'Home', headerShown: false }} 
                />
                <Tab.Screen 
                  name="Favourites" 
                  component={FavouritesStackScreen} 
                  options={{ tabBarLabel: 'Favourites', headerShown: true }} 
                />
                <Tab.Screen 
                  name="Faq" 
                  component={FaqStackScreen} 
                  options={{ 
                    tabBarLabel: 'FAQs', 
                    headerShown: true, 
                  }} 
                />
                <Tab.Screen 
                  name="Search" 
                  component={SearchStackScreen} 
                />
              </Tab.Navigator>
            </FavProvider>
          </NavigationContainer>
      </ApolloProvider>
      </SafeAreaProvider>
    );
  }
}

After the splash screen, and after the animated intro (see code below) , most of the time content appears immediately but sometimes randomly it takes up to 10-15s. After it does appear, any subsequent visit will work normal, for a while at least. I can't figure out if it's in-app bug or issue with server/database.

Here's a GIF illustrating the flow when it works properly:

enter image description here

Notice how list of Categories appear after "CHECK THIS OUT" Intro Image --- When the issue occurs, the Intro image fades-out, but the Categories don't appear for another 15s.

Using:

- Expo Go on iOS (only happens with expo publish, not in dev mode)

- Apollo and GraphQL to fetch content hosted on Heroku Postgres database.

- Heroku free dyno and Hobby-dev Postgres addon db (could this be the reason?)
  • If I minimize while this delay is ongoing, and come back to the app -- the Apollo useQuery hook will return an error.

  • if I don't minimize and just wait -- no error and it will eventually load.

  • It doesn't show the loading bar -- which should mean it's not stuck in "loading" state. Header and footer are visible - there's just empty screen where content should be.

HomeScreen.js:

import React, { useEffect, useState } from 'react'
import { View, Text, FlatList, Pressable, TouchableOpacity, ImageBackground, Image, Animated } from 'react-native'
import { useFocusEffect } from '@react-navigation/native';
import { gql, useQuery } from '@apollo/client'
import Bg from "../../../assets/background.png"
import Intro from "../../../assets/intro2x.png"
import { useFavContext } from '../../components/FavContext';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { StatusBar } from 'expo-status-bar';
import { LoadingLarge } from '../../components/Loading';

const HOME = gql`
  query HomeData {
    home {
      data {
        attributes {
          ...
        }
      }
    } 
  }
`  
const IntroMessageAnimation = (props) => {
    const fadeAnimat = React.useRef(new Animated.Value(0)).current; // Initial value for opacity: 0
    useFocusEffect(
        React.useCallback(() => {
            setTimeout(() => {
            Animated.timing(fadeAnimat, {
                toValue: 1,
                duration: 500,
                useNativeDriver: true,
            }).start();
            }, 500);
            setTimeout(() => {
                Animated.timing(fadeAnimat, {
                    toValue: 0,
                    duration: 500,
                    useNativeDriver: true,
                }).start();
            }, 2000);
        },[]))
    return (
        <Animated.View // Special animatable View
            key="1ab"
            style={{
            flex: 1,
            opacity: fadeAnimat, // Bind opacity to animated value
            justifyContent: 'center',
            alignItems: 'center'
            }}>
            {props.children}
        </Animated.View>
    );
}

const IntroMessage = () => (
    <IntroMessageAnimation >
        <Image
            style={{ 
                width: 273, 
                height: 324, 
            }}
            source={Intro}
            defaultSource={Intro}
            resizeMethod='scale'
            resizeMode='contain'
        />
    </IntroMessageAnimation >
)

const MainContentAnimation = (props) => {
    const fadeMainAnimat = React.useRef(new Animated.Value(0)).current; // Initial value for opacity: 0
    useEffect(() => { 
        const timeout = setInterval(() => {
            Animated.timing(fadeMainAnimat, {
                toValue: 1,
                duration: 500,
                useNativeDriver: true,
                }).start();
        }, 500);
        return () => clearInterval(timeout)
    })
    
    return (
      <Animated.View // Special animatable View
        style={{
          flex: 1,
          opacity: fadeMainAnimat, // Bind opacity to animated value
        }}>
        {props.children}
      </Animated.View>
    );
}

export default ({ navigation }) => {
    const insets = useSafeAreaInsets();
    const { favorites } = useFavContext()
    const { data, loading, error } = useQuery(HOME, {
        notifyOnNetworkStatusChange: true,
        fetchPolicy: 'network-only', // Used for first execution
        nextFetchPolicy: 'cache-first', // Used for subsequent executions
    })
    const [isReady, setIsReady] = useState(false)

    useEffect(() => {
        const timer = setTimeout(() => {
            setIsReady(true);
        }, 2700);
        return () => clearTimeout(timer);
    }, []);
      
    if ( !isReady || loading ) {
        return (
            <ImageBackground source={Bg} resizeMode="cover" style={tw.style('flex-1 relative')}>
                <StatusBar style='light'/>
                <IntroMessage/>
            </ImageBackground>
        )
    } else if ( loading ) {
        return <LoadingLarge />
    } 

    if (error) {
        return <Text>Error :( </Text>;
    }

    const heading = data.home.data.attributes.Heading;
    const subheading = data.home.data.attributes.Subheading;
    const categories = data.home.data.attributes.categories

    const MainScreen = () => (
        <MainContentAnimation >
            <View
                style={{
                   // ... inset paddings ...
                }}>
                <StatusBar style='light'/>
                <View>
                    <Text>{heading}</Text>
                    <Text>{subheading}</Text>
                </View>  
                <View>
                    <FlatList
                        showsVerticalScrollIndicator={false}
                        data={categories.data}
                        renderItem={({ item }) => (
                            <CategoryItem
                                favorites={favorites}
                                navigation={navigation}
                                category={item}
                                onPress={() => 
                                navigation.navigate('CategoryScreen', { item: item })}
                            />
                        )}
                        keyExtractor={(item) => item.id.toString()}
                    /> 
                </View>
            </View>
        </MainContentAnimation >
    )
    
    return (
        <ImageBackground source={Bg} resizeMode="cover" style={tw.style('flex-1 relative')}>
            <MainScreen/>
        </ImageBackground>
    )
}

If relevant, App.js:

// Initialize Apollo Client
const client = new ApolloClient({
  uri: 'https://janazaapp.herokuapp.com/graphql/',
  cache: new InMemoryCache({
    typePolicies: {
      Subsubcategory: {
        merge: true
      },
    },
  })
});

function HomeStackScreen() {
  return (
    <HomeStack.Navigator 
      initialRouteName="HomeScreen"
      screenOptions={({ navigation }) => {
        return {
          detachPreviousScreen: !navigation.isFocused(),
          headerShown: true, 
          headerMode: 'float',
        }
      }}
    >
      <HomeStack.Screen 
        name="HomeScreen" 
        component={FadeHomeScreen} 
        options={({ navigation }) => ({
          headerTitle: ({props}) => <LogoTitle onPress={() => navigation.navigate('HomeScreen')} />,
        })}
      />
      // ... other screrens ...
  </HomeStack.Navigator>
  );
}


  function MainScreen() {
    const ref = React.useRef(null);
    const newFavor = JSON.stringify(favor.current)

    return (
      <SafeAreaProvider>
      <ApolloProvider client={client}>
          <NavigationContainer theme={MyTheme} ref={ref}>
            <FavProvider favor={newFavor}>
              <Tab.Navigator
                screenOptions={({ route, navigation }) => ({
                    // ... screen options ...
                })}
              >
                <Tab.Screen 
                  name="Home" 
                  component={HomeStackScreen} 
                  options={{ tabBarLabel: 'Home', headerShown: false }} 
                />
                <Tab.Screen 
                  name="Favourites" 
                  component={FavouritesStackScreen} 
                  options={{ tabBarLabel: 'Favourites', headerShown: true }} 
                />
                <Tab.Screen 
                  name="Faq" 
                  component={FaqStackScreen} 
                  options={{ 
                    tabBarLabel: 'FAQs', 
                    headerShown: true, 
                  }} 
                />
                <Tab.Screen 
                  name="Search" 
                  component={SearchStackScreen} 
                />
              </Tab.Navigator>
            </FavProvider>
          </NavigationContainer>
      </ApolloProvider>
      </SafeAreaProvider>
    );
  }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文