I fetched videos from firebase storage and now displaying them in the app. I have 3 videos and problem is that every time I render the screen there is just one video rendered. UseState
for setVideoUrl
is assigning random number of video urls. Sometimes videoUrls
contain just one video and other time there is 2. I don’t understand why is it like that. Also useEffect is called several times. Can somebody help?
this is the code:
import React from "react";
import firebaseConfig from "../../../firebaseConfig";
import { View, Text, StyleSheet, Dimensions } from "react-native";
import { useEffect, useState } from "react";
import {getStorage, ref, listAll, getDownloadURL } from "firebase/storage"
import { Video } from "expo-av";
// import { WebView } from 'react-native-webview';
import { ActivityIndicator } from "react-native";
// import VideoPlayer from 'react-native-video';
const Program1 = () => {
const [videoUrls, setVideoUrls] = useState([]);
// const { width, height } = Dimensions.get("window");
const [loading, setLoading] = useState(true);
useEffect(() => {
FetchVideos();
}, []);
const FetchVideos = () => {
const storage = getStorage();
const listRef = ref(storage, "PersonligUtveckling/videos/");
listAll(listRef)
.then((res) => {
const urls = [];
res.items.forEach((itemRef) => {
getDownloadURL(itemRef)
.then((url) => {
console.log('URL of video file:', url);
urls.push(url);
setVideoUrls(urls);
setLoading(false);
})
});
})
.catch((error) => {
console.log(error);
});
}
const handleVideoError = (error) => {
console.log("Video error:", error);
};
const handleVideoLoad = () => {
console.log("Video loaded");
};
console.log(videoUrls.length);
if (loading) {
return (
<View style={[styles.container, styles.horizontal]}>
<ActivityIndicator size="large" />
{ loading && videoUrls.length === 0 ? <Text>No videos found</Text> : <ActivityIndicator size="large" />}
</View>
);
}
return (
<View style={styles.container}>
<Text>Hello</Text>
{!loading ?
videoUrls.map((url, index) => (
<Video
key={index}
isMuted={false}
volume={2.0}
source={{ uri: url }}
style={styles.video}
useNativeControls={true}
resizeMode="contain"
onError={handleVideoError}
onLoad={handleVideoLoad}
/>
)) : <ActivityIndicator size="large" />}
</View>
);
};
const styles = StyleSheet.create({
container: {
paddingTop: 40,
alignItems: 'center',
},
video: {
width: 320,
height: 240,
marginBottom: 20,
},
});
export default Program1;
Consol log:
URL of video file: https://firebasestorage.googleapis.com/v0/b/phantoms-mobile-app.appspot.com/o/PersonligUtveckling%2Fvideos%2Fvideo1.mp4?alt=media&token=a4bbedc2-bd17-428d-9bf4-267143af84fc
LOG 1
LOG 1
LOG URL of video file: https://firebasestorage.googleapis.com/v0/b/phantoms-mobile-app.appspot.com/o/PersonligUtveckling%2Fvideos%2Fvideo.mp4?alt=media&token=d2449d1f-7f03-481f-a1ca-989ba55cd243
LOG 2
LOG URL of video file: https://firebasestorage.googleapis.com/v0/b/phantoms-mobile-app.appspot.com/o/PersonligUtveckling%2Fvideos%2Fvideo2.mp4?alt=media&token=491ec16c-6994-45fb-9b76-40f575fa6df1
LOG Video loaded
2
Answers
You aren’t dealing with the asynchronous nature of this task correctly. You need to wait for all requests to be done before you set them into the state. Not doing so is causing race conditions on the
urls
array. But that array doesn’t need to exist. You need to keep everything in the promise chain.It seems like the issue is caused by the asynchronous nature of fetching the video URLs. You are using forEach to loop through the items, and for each item, you call an asynchronous function getDownloadURL(). This may cause the setVideoUrls() function to be called multiple times in an unpredictable order.
To fix this issue, you can use Promise.all() to ensure that all the download URLs are fetched before updating the state. Also, make sure you’re using a dependency array in your useEffect() to prevent it from being called multiple times.