react-native-calendars icon indicating copy to clipboard operation
react-native-calendars copied to clipboard

Using CalendarList, the date list is a bit slow to load

Open songxinglin98 opened this issue 9 months ago โ€ข 8 comments

The code is as follows. I got the date data of the last ten years. I put the time selection in the pop-up window. It will be a little slow to load every time, as shown in the first picture. The second one is the picture after loading. How to solve this problem?

<CalendarList markingType="period" markedDates={markedDates} onDayPress={handleDayPress} minDate={minDate} maxDate={maxDate} pastScrollRange={120} futureScrollRange={0} scrollEnabled={true} calendarWidth={width} calendarHeight={300} hideDayNames={true} // ้š่—ๅ†…็ฝฎ็š„ๆ˜ŸๆœŸๅ็งฐ monthFormat={'yyyyๅนด MMๆœˆ'} />

Image

Image

songxinglin98 avatar May 13 '25 06:05 songxinglin98

Same here.

luco avatar May 21 '25 03:05 luco

+1

abdelwehed avatar May 21 '25 12:05 abdelwehed

to add a loading mask on calendar view and set theme to hide the date when loading 'stylesheet.calendar-list.main': { placeholderText: { // text styles color: 'transparent', }, },

onlybenyang avatar May 30 '25 07:05 onlybenyang

Same here, I don't know if there is a way of preloading it before using it?

Mr-Yaya avatar May 30 '25 14:05 Mr-Yaya

I "solved" this by preloading the calendar component off-screen while the user is on a different tab/view.

The Fix:

// Preload CalendarList invisibly while on other view {shouldPreload && ( <View style={{ position: 'absolute', top: -2000, opacity: 0, pointerEvents: 'none' }}> <CalendarList {...calendarProps} /> </View> )}

When user switches to calendar view, the component is already rendered in memory - no more flash.

Delay the preload by ~1 second to avoid affecting initial load performance.

LucasZNK avatar Jun 26 '25 14:06 LucasZNK

'stylesheet.calendar-list.main': { placeholderText: { // text styles color: 'transparent', }, },

What am I doing wrong since this doesn't help and the date string is still visible?

<CalendarList ref={calendarRef} pastScrollRange={6} futureScrollRange={0} firstDay={1} scrollEnabled showScrollIndicator markingType="period" markedDates={...} onDayPress={...} monthFormat="MMMM yyyy" style={...} theme={{ stylesheet: { 'calendar-list': { main: { placeholderText: { color: 'transparent', }, }, }, }, }} />

Mykhailo-NLtDev avatar Jul 11 '25 15:07 Mykhailo-NLtDev

same problem here

itsandrehere avatar Aug 31 '25 17:08 itsandrehere

Here is the solution to this:

const Calendar = () => {
	const [startDate, setStartDate] = React.useState<string | null>(null);
	const [endDate, setEndDate] = React.useState<string | null>(null);
	const [isCalendarLoading, setIsCalendarLoading] = React.useState(true);

	React.useEffect(() => {
		const timer = setTimeout(() => {
			setIsCalendarLoading(false);
		}, 200); // 2 second loading

		return () => clearTimeout(timer);
	}, []);

	const handleDayPress = (day: { dateString: string }) => {
		const dateString = day.dateString;

		// If no start date is selected, set it as start date
		if (!startDate) {
			setStartDate(dateString);
			setEndDate(null);
			return;
		}

		// If start date is selected but no end date
		if (startDate && !endDate) {
			// If the selected date is before start date, make it the new start date
			if (new Date(dateString) < new Date(startDate)) {
				setStartDate(dateString);
				setEndDate(startDate);
			} else {
				// Set as end date
				setEndDate(dateString);
			}
			return;
		}

		// If both dates are selected, reset and start over
		setStartDate(dateString);
		setEndDate(null);
	};

	const getMarkedDates = () => {
		const marked: Record<
			string,
			{
				startingDay?: boolean;
				endingDay?: boolean;
				color: string;
				textColor: string;
			}
		> = {};

		if (!startDate) return marked;

		// Mark start date
		marked[startDate] = {
			startingDay: true,
			color: "#FF6B35",
			textColor: "#FFFFFF",
		};

		if (!endDate) return marked;

		// Mark end date
		marked[endDate] = {
			endingDay: true,
			color: "#FF6B35",
			textColor: "#FFFFFF",
		};

		// Mark dates in between
		const start = new Date(startDate);
		const end = new Date(endDate);
		const current = new Date(start);

		while (current < end) {
			current.setDate(current.getDate() + 1);
			const dateString = current.toISOString().split("T")[0];

			if (dateString !== endDate) {
				marked[dateString] = {
					color: "#FFE5D9",
					textColor: "#FF6B35",
				};
			}
		}

		return marked;
	};

	const markedDates = getMarkedDates();

	// Theme with transparent dates when loading
	const calendarTheme = {
		backgroundColor: "transparent",
		calendarBackground: "transparent",
		textSectionTitleColor: "#6B7280",
		selectedDayBackgroundColor: "#FF6B35",
		selectedDayTextColor: "#FFFFFF",
		todayTextColor: "#FF6B35",
		dayTextColor: "#374151",
		textDisabledColor: "#D1D5DB",
		dotColor: "#FF6B35",
		selectedDotColor: "#FFFFFF",
		arrowColor: "#FF6B35",
		monthTextColor: "#111827",
		indicatorColor: "#FF6B35",
		textDayFontFamily: "System",
		textMonthFontFamily: "System",
		textDayHeaderFontFamily: "System",
		textDayFontWeight: "400" as const,
		textMonthFontWeight: "600" as const,
		textDayHeaderFontWeight: "500" as const,
		textDayFontSize: 16,
		textMonthFontSize: 18,
		textDayHeaderFontSize: 14,
		...(isCalendarLoading && {
			"stylesheet.calendar-list.main": {
				placeholderText: {
					color: "transparent",
				},
			},
		}),
	};

	return (
		<View style={styles.root}>
			<View style={styles.calendarContainer}>
				<CalendarList
					onDayPress={handleDayPress}
					onVisibleMonthsChange={(months) => {
						console.log("now these months are visible", months);
					}}
					pastScrollRange={12}
					futureScrollRange={12}
					scrollEnabled={!isCalendarLoading}
					showScrollIndicator={false}
					markedDates={isCalendarLoading ? {} : markedDates}
					calendarHeight={340}
					calendarWidth={Dimensions.get("window").width - SAFE_AREA_VIEW_PADDING * 2}
					removeClippedSubviews={true}
					initialNumToRender={1}
					maxToRenderPerBatch={1}
					windowSize={5}
					theme={calendarTheme}
					markingType="period"
				/>
				{isCalendarLoading && <View style={styles.loadingMask}>{/* Calendar Skeleton */}</View>}
			</View>
		</View>
	);
};

const styles = StyleSheet.create({
	root: {
		flex: 1,
		backgroundColor: "#FFF",
	},
	calendarContainer: {
		flex: 1,
		paddingTop: 20,
		paddingBottom: 20,
		alignItems: "center",
		justifyContent: "center",
		position: "relative",
	},
	loadingMask: {
		position: "absolute",
		top: 0,
		left: 0,
		right: 0,
		bottom: 0,
		backgroundColor: "rgba(255, 255, 255, 0.9)",
		justifyContent: "center",
		alignItems: "center",
		zIndex: 10,
	},
});

export default Calendar;

ugi-dev avatar Oct 16 '25 11:10 ugi-dev