Search code examples
react-native

Refreshing the state daily in React Native


I need to show a daily video in my React Native app. This is a randomized video from a local JSON file that has the thumbnail, title, and URL. I have it so that it modifies the state at 00:00:00 hours with a random video from the JSON file. However, on initial load, the state is showing empty. How can I make the randomize function fire once (when they install the app), but only randomize on 00:00:00 hours after that?

import AsyncStorage from '@react-native-async-storage/async-storage';
import dailyvideo from '../assets/data/dailyvideo';

const [time, setTime] = useState(new Date());

const INITIAL_STATE = {
  title: 'Seoi Nage',
  url: 'seoinage.mp4',
  thumbnail: "seionage.jpeg"
}

const [newvideo, setNewVideo] = useState(INITIAL_STATE)

const getTheState = async () => {
  const videotitle = await AsyncStorage.getItem("title")
  const videourl = await AsyncStorage.getItem("url")
  const videothumbnail = await AsyncStorage.getItem("thumbnail")

  setNewVideo({
      title: videotitle,
      url: videourl,
      thumbnail: videothumbnail
    }
  )
}

const handleRandomVideo = async () => {
  const randomIndex = Math.floor(Math.random() * allwaza.length);
  const randomItem = allwaza[randomIndex];

  try {
    await AsyncStorage.multiSet(
      [
        ["title", randomItem.title],
        ["url", randomItem.url],
        ["thumbnail", randomItem.thumbnail]
      ]
    )
  } catch (error) {
    console.error(error);
  }
}


useEffect(() => {
  const interval = setInterval(() => {
    setTime(new Date());
  }, 1000);

  return () => {
    clearInterval(interval);
  };
}, []);


useEffect(() => {
  if (time.getHours() === 0 && time.getMinutes() === 0 && time.getSeconds() ===0) {
    handleRandomVideo()
  }

  getTheState()

}, [time]);

return (
   <View>
      <Text>Today's Suggested Tech </Text>
      <View>
        // This fires a VideoPlayer component that handles the URL
        <TouchableOpacity onPress={() => navigation.navigate('VideoPlayer', {data: newvideo})}>
          <Image source={{uri: newvideo.thumbnail}}/>
        </TouchableOpacity>
        <Text> {newvideo.title} </Text>
      </View>
   </View>
)

For clarification, the video shows up and plays when it is set to storage. My issue is, on a fresh install, the storage is empty until the next 00:00:00 hours hit. It's bad UX. I need it to run immediately on a fresh install and then at 00:00:00 after that.


Solution

  • To ensure that the randomization function fires immediately upon fresh install and only at 00:00:00 hrs afterward, you can utilize a combination of useEffect and the AsyncStorage API.

    Here's how you can modify your code to achieve this:

    import React, { useState, useEffect } from 'react';
    import { View, Text, Image, TouchableOpacity } from 'react-native';
    import AsyncStorage from '@react-native-async-storage/async-storage';
    import dailyvideo from '../assets/data/dailyvideo';
    
    const INITIAL_STATE = {
      title: 'Seoi Nage',
      url: 'seoinage.mp4',
      thumbnail: 'seionage.jpeg'
    };
    
    const VideoComponent = ({ navigation }) => {
      const [time, setTime] = useState(new Date());
      const [newvideo, setNewVideo] = useState(INITIAL_STATE);
    
      const allwaza = dailyvideo; // assuming allwaza is your JSON data
    
      const handleRandomVideo = async () => {
        const randomIndex = Math.floor(Math.random() * allwaza.length);
        const randomItem = allwaza[randomIndex];
    
        try {
          await AsyncStorage.multiSet([
            ['title', randomItem.title],
            ['url', randomItem.url],
            ['thumbnail', randomItem.thumbnail]
          ]);
          setNewVideo(randomItem);
        } catch (error) {
          console.error(error);
        }
      };
    
      const getTheState = async () => {
        const videotitle = await AsyncStorage.getItem('title');
        const videourl = await AsyncStorage.getItem('url');
        const videothumbnail = await AsyncStorage.getItem('thumbnail');
    
        if (videotitle && videourl && videothumbnail) {
          setNewVideo({
            title: videotitle,
            url: videourl,
            thumbnail: videothumbnail
          });
        } else {
          handleRandomVideo();
        }
      };
    
      useEffect(() => {
        const checkAndSetVideo = async () => {
          await getTheState();
          const interval = setInterval(() => {
            setTime(new Date());
          }, 1000);
    
          return () => {
            clearInterval(interval);
          };
        };
    
        checkAndSetVideo();
      }, []);
    
      useEffect(() => {
        if (
          time.getHours() === 0 &&
          time.getMinutes() === 0 &&
          time.getSeconds() === 0
        ) {
          handleRandomVideo();
        }
      }, [time]);
    
      return (
        <View>
          <Text>Today's Suggested Tech </Text>
          <View>
            <TouchableOpacity
              onPress={() => navigation.navigate('VideoPlayer', { data: newvideo })}
            >
              <Image source={{ uri: newvideo.thumbnail }} />
            </TouchableOpacity>
            <Text>{newvideo.title}</Text>
          </View>
        </View>
      );
    };
    
    export default VideoComponent;
    

    Here's a breakdown of what's happening:

    • The checkAndSetVideo function is created to fetch video data from AsyncStorage. If it's not available, it triggers the randomization function. This function is then invoked within the useEffect with an empty dependency array, ensuring it runs only once on component mount.
    • The useEffect hook with the time dependency is responsible for checking whether it's 00:00:00 hrs. If so, it triggers the randomization function.
    • In this setup, the video will be shown immediately upon the first install, and thereafter, it will randomize at 00:00:00 hrs.