'React-Native — How to select a start and end date using react-native-calendars (WIX)?
I am trying to enable date range using react-native-calendars. On my app, the calendar loads a few 'markedDates'; now I need to implement a start and end date functionality without affecting these initial dates. Unfortunately, I am struggling to achieve that. Any ideas on how can I do that?
Thank you in advance.
Pseudo-code
- Load calendar with marked dates
- Tap on start date
- Tap on end date
- Continue
Component
export default class Dates extends Component {
static navigationOptions = {
title: 'Choose dates',
}
constructor(props) {
super(props)
this.state = {
selected: undefined,
marked: undefined,
}
}
componentDidMount() {
this._markDate()
}
_markDate = () => {
nextDay = []
const marked = {
[nextDay]: { selected: true, marked: true },
}
Util._findShows(resp => {
resp.map(data => {
nextDay.push(data.date)
})
var obj = nextDay.reduce((c, v) => Object.assign(c, { [v]: { marked: true, dotColor: 'black' } }), {})
this.setState({ marked: obj })
})
}
_selectDate = obj => {
this.setState({ selected: obj.dateString })
}
render() {
return (
<View style={styles.container}>
<CalendarList
// Callback which gets executed when visible months change in scroll view. Default = undefined
onVisibleMonthsChange={months => {
console.log('now these months are visible', months)
}}
// Max amount of months allowed to scroll to the past. Default = 50
pastScrollRange={0}
// Max amount of months allowed to scroll to the future. Default = 50
futureScrollRange={12}
// Enable or disable scrolling of calendar list
scrollEnabled={true}
// Enable or disable vertical scroll indicator. Default = false
showScrollIndicator={true}
markedDates={
// [this.state.selected]: { selected: true, disableTouchEvent: true, selectedDotColor: 'orange' },
this.state.marked
}
onDayPress={day => {
this._selectDate(day)
}}
/>
<View style={styles.ctaArea}>
<TouchableOpacity style={styles.button} onPress={() => this.props.navigation.navigate('Dates')}>
<Text style={styles.btTitle}>Continue</Text>
</TouchableOpacity>
</View>
</View>
)
}
}
Solution 1:[1]
I had the same struggle but so I decided to make my own version.
I'm sure it can be done better and have more functionalities but it works okay for me
const [dates, setDates] = useState({});
// get array of all the dates between start and end dates
var getDaysArray = function(start, end) {
for (var arr=[], dt = new Date(start); dt <= new Date(end); dt.setDate(dt.getDate() + 1)){
arr.push(useFormatDate(new Date(dt)));
}
return arr;
};
// empty object
const emptyObj = (obj: Object) => {
var props = Object.getOwnPropertyNames(obj);
for (var i = 0; i < props.length; i++) {
delete dates[props[i]];
}
}
// check if first date is smaller or equals to second date
const compareDate = function(first: string, second: string) {
return (new Date(first) <= new Date(second) ? true : false);
}
// fill with color the date between first and second date selected
const fillRangeDate = function(first: string, second: string) {
emptyObj(dates);
let newDates = dates;
newDates[first]={selected: true, Colors[colorScheme].tint};
newDates[second]={selected: true, color: Colors[colorScheme].tint};
var range = getDaysArray(first, second);
for (var i = 1; i < range.length - 1; i++)
newDates[range[i]]={color: '#70d7c7', textColor: 'white'};
setDates({...newDates})
}
const selectDate = (day) => {
let selectedDate = day.dateString;
let newDates = dates;
// if 2 dates are selected
if (Object.keys(dates).length >= 2) {
var props = Object.getOwnPropertyNames(dates);
if (compareDate(props[0], selectedDate)) {
fillRangeDate(props[0], selectedDate);
} else {
emptyObj(dates);
}
} else {
// 1 date selected
if (Object.keys(dates).length == 0) {
newDates[selectedDate]={selected: true, color: Colors[colorScheme].tint};
} else if (Object.keys(dates).length == 1) { // 2 dates selected
newDates[selectedDate]={selected: true, color: Colors[colorScheme].tint};
// if 2nd date < 1st date, cancel range
var props = Object.getOwnPropertyNames(dates);
if (compareDate(props[0], props[1])) {
var range = getDaysArray(props[0], props[1]);
for (var i = 1; i < range.length - 1; i++) {
newDates[range[i]]={color: '#70d7c7', textColor: 'white'};
}
} else {
emptyObj(dates);
}
}
}
setDates({...newDates})
}
You'll also need to add this function that I implemented as a hook:
const useFormatDate = (date: Date) => {
function padTo2Digits(num) {
return num.toString().padStart(2, '0');
}
return [
date.getFullYear(),
padTo2Digits(date.getMonth() + 1),
padTo2Digits(date.getDate()),
].join('-');
};
Help me to improve this code and maybe create a merge request on the wix/react-native-calendar
Hope this helps
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Retwix |