skip to Main Content

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


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

    const FetchVideos = () => {
      const storage = getStorage();
      const listRef = ref(storage, "PersonligUtveckling/videos/");
    
      listAll(listRef)
        .then((res) =>
          Promise.all(
            res.items.map((itemRef) =>
              getDownloadURL(itemRef).then((url) => {
                console.log("URL of video file:", url);
                return url;
              })
            )
          )
        )
        .then((urls) => setVideoUrls(urls))
        .catch((error) => {
          console.log(error);
        })
        .finally(() => setLoading(false));
    };
    
    
    Login or Signup to reply.
  2. 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.

    useEffect(() => {
      FetchVideos();
    }, []); // Add an empty dependency array to ensure useEffect is called only once.
    
    const FetchVideos = async () => {
      const storage = getStorage();
      const listRef = ref(storage, "PersonligUtveckling/videos/");
    
      try {
        const res = await listAll(listRef);
        const urlPromises = res.items.map((itemRef) => getDownloadURL(itemRef));
        const urls = await Promise.all(urlPromises);
        console.log('URLs of video files:', urls);
        setVideoUrls(urls);
        setLoading(false);
      } catch (error) {
        console.log(error);
      }
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search