skip to Main Content

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;

2

Answers


  1. I think you are overcomplicating things. First of all you don’t need to create all new individual Date objects just to get the date, month and year. Secondly, you can get the date formatted the way you want by using toLocaleDateString() and specifying the locale for the style of formatting you want. And thirdly, the native JavaScript Date object handles month and year boundaries automatically if you use setDate().

    const today = new Date();
    const day = today.getDate();
    const month = today.getMonth() + 1;
    const year = today.getFullYear();
    
    // but you don't even need to do all that
    // you can get it formatted for your locale
    today.toLocaleDateString('en-NZ'); // '16/12/2022'
    
    // initially this will be today
    const tomorrow = new Date();
    tomorrow.setDate(tomorrow.getDate() + 1);
    
    // initially this will be today
    const yesterday = new Date();
    yesterday.setDate(yesterday.getDate() - 1);
    
    // it automatically handles month and year boundaries
    const yearEnd = new Date('12/31/2022');
    console.log(yearEnd); // Sat Dec 31 2022 00:00:00 GMT-0500 (Eastern Standard Time)
    
    yearEnd.setDate(yearEnd.getDate() + 1);
    console.log(yearEnd);  // Sun Jan 01 2023 00:00:00 GMT-0500 (Eastern Standard Time)
    
    Login or Signup to reply.
  2. 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>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search