ReactFix
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • React
  • React Native
  • Programming
  • Object Oriented Programming

Wednesday, December 21, 2022

[FIXED] How do I generate future dates using vanilla JavaScript and/or state?

 December 21, 2022     javascript, react-native, reactjs   

Issue

I'm building a React Native gig guide app.

Each gig is represented by a document in the firebase collection and has an associated date property.

I want to display the current day's gigs to the user. I also the user to tap a "next day's gigs" button and be shown a list of gigs for the next day, as follows:

Home page UI

I can get the current day's gigs without issue using the following logic:

  const day = new Date().getDate();
  const month = new Date().getMonth() + 1;
  const year = new Date().getFullYear();
  const dateToday = `${day}/${month}/${year}`;


  //Filtering through gigs to return only current day's gigs
  const gigsToday = gigs.filter((gig) => gig.date === dateToday);

...but how do I show the next/previous day's gigs?

Here's what I've done so far

1.) Convert the new Date() to a UTC+13 time:

  const addHours = (numOfHours, date = new Date()) => {
    date.setTime(date.getTime() + numOfHours * 60 * 60 * 1000);
    return date;
  };

  let localDate = addHours(13);

2.) Set up an onPress event that increments the state variable daysAdded by 1:

        <Pressable onPress={addDay}>
          <Text style={styles.buttonOptionsText}>next day's gigs</Text>
        </Pressable>

3.) Create a function that adds a day to the localDate variable, and then set that new date to state variable date:

const [date, setDate] = useState(dateToday);

...

  const addDay = () => {
    setDaysAdded(daysAdded + 1);
    localDate.setDate(localDate.getDate() + daysAdded);
    setDate(localDate);
  };

The problem is that the initial date state is not immediately loaded, which means I can't conditionally render dates based on their date. I'm confused about something else - I have a date that I want to manipulate, so do I set this date as a variable or a piece of state?

Anyway, ideally once the user presses the "next days gigs" button, the app will conditionally render the gigs for the next day.

I should also mention that the date being returned from my Firebase Firestore is in the form "DD/MM/YYYY"

Full code is as follows:

GigMap.js

import { useState, useEffect } from "react";
import { StyleSheet, Text, View, Pressable } from "react-native";
import MapView from "react-native-maps";
import { Marker, Callout } from "react-native-maps";
import CalloutView from "./CalloutView";
import { mapStyle } from "../util/mapStyle";
import { useGigs } from "../hooks/useGigs";

const GigMap = ({ navigation }) => {

  const [date, setDate] = useState(dateToday);
  const gigs = useGigs()
  const [daysAdded, setDaysAdded] = useState(1);



  const addHours = (numOfHours, date = new Date()) => {
    date.setTime(date.getTime() + numOfHours * 60 * 60 * 1000);
    return date;
  };

  let localDate = addHours(13);

  useEffect(() => {
    setDate(localDate);
  }, []);

  const addDay = () => {
    setDaysAdded(daysAdded + 1);
    localDate.setDate(localDate.getDate() + daysAdded);
    setDate(localDate);
  };

  //Generating current date
  const day = new Date().getDate();
  const month = new Date().getMonth() + 1;
  const year = new Date().getFullYear();
  const dateToday = `${day}/${month}/${year}`;


  //Filtering through gigs to return only current day's gigs
  const gigsToday = gigs.filter((gig) => gig.date === dateToday);

  return (
    <View style={styles.container}>
      <Text style={styles.headerText}>Today's gigs</Text>
      <MapView
        initialRegion={{
          latitude: -41.29416,
          longitude: 174.77782,
          latitudeDelta: 0.03,
          longitudeDelta: 0.03,
        }}
        style={styles.map}
        customMapStyle={mapStyle}
      >
        {gigsToday.map((gig, i) => (
          <Marker
            key={i}
            coordinate={{
              latitude: gig.location.latitude,
              longitude: gig.location.longitude,
            }}
            image={require("../assets/Icon_Gold_48x48.png")}
          >
            <Callout
              style={styles.callout}
              onPress={() =>
                navigation.navigate("GigDetails", {
                  venue: gig.venue,
                  date: gig.date,
                  gigName: gig.gigName,
                  time: gig.time,
                })
              }
            >
              <CalloutView
                venue={gig.venue}
                date={gig.date}
                gigName={gig.gigName}
                time={gig.time}
                style={styles.calloutView}
              />
            </Callout>
          </Marker>
        ))}
      </MapView>
      <View style={styles.buttonOptions}>
        <Pressable>
          <Text style={styles.buttonOptionsText}>previous day's gigs</Text>
        </Pressable>
        <Pressable onPress={addDay}>
          <Text style={styles.buttonOptionsText}>next day's gigs</Text>
        </Pressable>
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flexDirection: "column",
    alignItems: "center",
  },
  map: {
    height: 500,
    width: 330,
    margin: 10,
  },
  headerText: {
    color: "black",
    fontSize: 20,
    marginTop: 5,
  },
  callout: {
    width: 200,
    height: 100,
  },
  buttonOptions: {
    flexDirection: "row",
    justifyContent: "flex-start",
  },
  buttonOptionsText: {
    margin: 5,
  },
});

export default GigMap;


Solution

Your code is not timezones-aware and it has a lot of logic and variables outside of the react state management functions which could cause bugs due to the fact that variables will be recreated and functions will be called on each render. You should use useState and useMemo more frequently. Do not rely on raw variables and functions calls until you clearly know what you are doing.

const {useState, useMemo} = React;

const useGigs = () => {
  return [
    { date: "13/12/2022", someValue: 10 },
    { date: "14/12/2022", someValue: 10 },
    { date: "15/12/2022", someValue: 10 },
    { date: "16/12/2022", someValue: 10 },
    { date: "15/12/2022", someValue: 11 },
    { date: "17/12/2022", someValue: 12 },
    { date: "18/12/2022", someValue: 12 },
    { date: "19/12/2022", someValue: 12 },
    { date: "20/12/2022", someValue: 12 },
    { date: "21/12/2022", someValue: 12 }
  ];
};

function App() {
  const gigs = useGigs();
  // set initial date to Today (now) in milliseconds.
  const [selectedDateMs, setSelectedDateMs] = useState(Date.now());

  // Or this
  const selectedDateString = useMemo(() => {
    const d = new Date(selectedDateMs);
    const day = d.getDate();
    const month = d.getMonth() + 1;
    const year = d.getFullYear();
    return `${day}/${month}/${year}`;
  }, [selectedDateMs]);

  // Or this
  const selectedDateUTCString = useMemo(() => {
    const d = new Date(selectedDateMs);
    const day = d.getUTCDate();
    const month = d.getUTCMonth() + 1;
    const year = d.getUTCFullYear();
    return `${day}/${month}/${year}`;
  }, [selectedDateMs]);

  const filteredGigs = useMemo(() => {
    return gigs.filter((gig) => gig.date === selectedDateString);
  }, [gigs, selectedDateString]);

  const addDays = (amount) => {
    // 1000ms * 60s * 60mins * 24hrs * (amount of day to add or substract)
    setSelectedDateMs((curr) => curr + 1000 * 60 * 60 * 24 * amount);
  };

  return (
    <div className="App">
      <hr />
      <p>Selected date (UTC): {new Date(selectedDateMs).toISOString()}</p>
      <p>Selected date (user local): {new Date(selectedDateMs).toString()}</p>
      <p>Current date (user timezone) string: {selectedDateString}</p>
      <p>Current date (UTC) string: {selectedDateUTCString}</p>
      <hr />
      <p>Gigs for selected day:</p>
      <pre>{JSON.stringify(filteredGigs)}</pre>
      <hr />
      <div>
        <button type="button" onClick={() => addDays(-1)}>
          Prev day
        </button>
        <button type="button" onClick={() => addDays(1)}>
          Next day
        </button>
      </div>
    </div>
  );
}



// v18.x+
ReactDOM.createRoot(
    document.getElementById("root")
).render(
    <App />
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

<div id="root"></div>



Answered By - Sergey Sosunov
Answer Checked By - - Terry (ReactFix Volunteer)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Google+
  •  Stumble
  •  Digg
Newer Post Older Post Home

Featured Post

Is Learning React Difficult?

React is difficult to learn, no ifs and buts. React isn't inherently difficult to learn. It's a framework that's different from ...

Total Pageviews

Copyright © ReactFix