在React Native Expo应用中加载内容的神秘随机延迟
在启动屏幕和动画介绍之后(请参见下面的代码)之后,大多数时间内容都会立即出现,但有时随机占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:
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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论