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:
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;
CodePudding user response:
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)
CodePudding user response:
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>