After I have selected a place from suggestions and the data is fetched from Firestore collection or the Maps API (based on the search input box), it shows the place details in selected venue details correctly.
However, when I refocus on the search venue input field to retype the query after the selected venue details have been fetched, the screen freezes and I receive this warning:
WARN Excessive number of pending callbacks: 501. Some pending callbacks that might have leaked by never being called from native code: {"3190":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3193":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3196":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3199":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3202":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3205":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3208":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3211":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3214":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3217":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3220":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3223":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3226":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3229":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3232":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3235":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3238":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3241":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3244":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3247":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3250":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3253":{"module":"UIManager","method":"configureNextLayoutAnimation"},"3256":{"module":"UIManager","method":"configureNextLayoutAnimation"},"...(truncated keys)...":451}
Here is my code:
import React, { useCallback, useState, useReducer, useEffect, useMemo, useContext } from "react";import { View, Text, TextInput, Button, StyleSheet, TouchableOpacity, Modal, Platform, ScrollView, FlatList, Image, ActivityIndicator, Alert, KeyboardAvoidingView } from 'react-native';import RNPickerSelect from 'react-native-picker-select';import Icon from 'react-native-vector-icons/Feather';import { COLORS, FONTS, SIZES, images } from "../../constants";import { useDispatch, useSelector } from 'react-redux';// more imports const CreateTour = ({ navigation, route }) => { const { currentUser, userData, updateUserData } = useContext(AuthContext); const { selectedTour, setSelectedTour, tours, setTourDetails, setTours } = useContext(TourContext); console.log('Current User:', currentUser); console.log('User Data:', userData); const { tourData, initialStep } = route.params || {}; const [currentStep, setCurrentStep] = useState(initialStep || 1);// more const handleSubmit = async () => { if (!db) return; // Return if db is not initialized yet setIsSubmitting(true); // Set isSubmitting to true when the form submission starts // Store selected tour dates in the 'tourDates' subcollection const tourDatesRef = collection(tourRef, 'tourDates'); // Get the existing tour dates from Firestore const existingTourDatesSnapshot = await getDocs(tourDatesRef); const existingTourDates = existingTourDatesSnapshot.docs.map(doc => doc.data().date); // Delete removed tour dates from Firestore const removedDates = existingTourDates.filter(date => !selectedDates.some(selectedDate => selectedDate.toISOString().split('T')[0] === date)); for (const removedDate of removedDates) { const removedDateDoc = existingTourDatesSnapshot.docs.find(doc => doc.data().date === removedDate); if (removedDateDoc) { await deleteDoc(removedDateDoc.ref); } } for (let i = 0; i < selectedDates.length; i++) { const date = selectedDates[i]; const dateString = date.toISOString().split('T')[0]; const dateId = `date${i + 1}`; const dateRef = doc(tourDatesRef, dateId); await setDoc(dateRef, { date: dateString, event: { venueDetails: { venueData: venueDetails[dateString] || {}, }, }, }); // Store event details for the particular date const eventData = { venueDetails: { venueData: { id: venueDetails[dateString]?.id || '', name: venueDetails[dateString]?.name || '', displayName: venueDetails[dateString]?.displayName || '', formattedAddress: venueDetails[dateString]?.formattedAddress || '', placeCoverPhotoUrl: venueDetails[dateString]?.placeCoverPhotoUrl || '', shortFormattedAddress: venueDetails[dateString]?.shortFormattedAddress || '', adrFormatAddress: venueDetails[dateString]?.adrFormatAddress || '', addressComponents: venueDetails[dateString]?.addressComponents || '', location: venueDetails[dateString]?.location || '', plusCode: venueDetails[dateString]?.plusCode || '', types: venueDetails[dateString]?.types || [], viewport: venueDetails[dateString]?.viewport || null, accessibilityOptions: venueDetails[dateString]?.accessibilityOptions || {}, businessStatus: venueDetails[dateString]?.businessStatus || '', googleMapsUri: venueDetails[dateString]?.googleMapsUri || '', iconBackgroundColor: venueDetails[dateString]?.iconBackgroundColor || '', iconMaskBaseUri: venueDetails[dateString]?.iconMaskBaseUri || '', primaryType: venueDetails[dateString]?.primaryType || '', primaryTypeDisplayName: venueDetails[dateString]?.primaryTypeDisplayName || '', subDestinations: venueDetails[dateString]?.subDestinations || '', utcOffsetMinutes: venueDetails[dateString]?.utcOffsetMinutes || '', currentOpeningHours: venueDetails[dateString]?.currentOpeningHours || '', currentSecondaryOpeningHours: venueDetails[dateString]?.currentSecondaryOpeningHours || '', internationalPhoneNumber: venueDetails[dateString]?.internationalPhoneNumber || '', nationalPhoneNumber: venueDetails[dateString]?.nationalPhoneNumber || '', priceLevel: venueDetails[dateString]?.priceLevel || '', rating: venueDetails[dateString]?.rating || '', regularOpeningHours: venueDetails[dateString]?.regularOpeningHours || '', regularSecondaryOpeningHours: venueDetails[dateString]?.regularSecondaryOpeningHours || '', userRatingCount: venueDetails[dateString]?.userRatingCount || '', websiteUri: venueDetails[dateString]?.websiteUri || '', }, }, }; await setDoc(dateRef, { date: dateString, event: eventData }, { merge: true }); // Update the tours array in the TourContextsetTours(prevTours => { const updatedTours = prevTours.map(tour => tour.id === updatedTourData.id ? updatedTourData : tour); return updatedTours;}); // Navigate back to the Dates screen navigation.navigate('TourManagerDashboard', { screen: 'Dates', params: { updatedTour: updatedTourData }, }); } else { console.error('Updated tour document not found'); } } catch (error) { console.error('Error submitting form:', error); } setIsSubmitting(false);}; const renderDateTabs = () => { return (<View style={styles.dateTabsContainer}> {selectedDates.map((date, index) => (<TouchableOpacity key={index} style={[styles.dateTab, selectedDateIndex === index && styles.selectedDateTab]} onPress={() => setSelectedDateIndex(index)}><Text style={styles.dateTabDay}>{getDayOfWeek(date)}</Text><Text style={styles.dateTabText}>{getDateText(date)}</Text></TouchableOpacity> ))}</View> ); }; const fetchSuggestions = async (query) => { try { console.log('Fetching suggestions for query:', query); console.log('Session token:', sessionToken); // Default latitude and longitude for India let latitude = 20.593684; // Latitude of India let longitude = 78.96288; // Longitude of India // Try to get the user's current location if (navigator.geolocation) { const position = await new Promise((resolve, reject) => { navigator.geolocation.getCurrentPosition(resolve, reject); }); latitude = position.coords.latitude; longitude = position.coords.longitude; } const response = await fetch('https://places.googleapis.com/v1/places:autocomplete', { method: 'POST', headers: {'Content-Type': 'application/json','X-Goog-Api-Key': 'key', }, body: JSON.stringify({ input: query, locationBias: { circle: { center: { latitude: latitude, longitude: longitude, }, radius: 5000, }, }, sessionToken: sessionToken, }), }); const data = await response.json(); console.log('Autocomplete response:', data); if (data.suggestions && data.suggestions.length > 0) { setSuggestions(data.suggestions); console.log('Suggestions set:', data.suggestions); } else { setSuggestions([]); console.log('No suggestions found'); } } catch (error) { console.error('Error fetching suggestions:', error); Alert.alert('Error', 'Failed to fetch suggestions.'); } }; const handleSearchVenue = useCallback( debounce((text) => { setSearchQuery(text); console.log('Search query changed:', text); if (text.length > 0) { fetchSuggestions(text); } else { setSuggestions([]); console.log('Search query is empty, clearing suggestions'); } }, 1500, { leading: false, trailing: true }), [] ); useEffect(() => { const generateSessionToken = () => { const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { const r = (Math.random() * 16) | 0; const v = c === 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); setSessionToken(uuid); console.log('Session token generated:', uuid); }; if (searchQuery) { generateSessionToken(); } else { setSessionToken(''); } return () => { setSessionToken(''); }; }, [searchQuery]); const handleVenueSearch = async () => { if (!db) return; // Return if db is not initialized yet try { const placesRef = collection(db, 'Places'); const venueQuery = query( placesRef, where('displayName', '>=', venueSearchQuery), where('displayName', '<=', venueSearchQuery +'\uf8ff'), limit(5) ); const venueSnapshot = await getDocs(venueQuery); if (venueSnapshot.empty) { setVenueSearchResults([]); } else { const venueData = venueSnapshot.docs.map(doc => ({ id: doc.id, ...doc.data(), })); setVenueSearchResults(venueData); } } catch (error) { console.error('Error searching venues:', error); } }; const renderDateFields = () => { const selectedDate = selectedDates[selectedDateIndex]; if (!selectedDate) { return null; // Return null or a placeholder component when selectedDate is undefined } const dateString = selectedDate.toISOString().split('T')[0]; const handleVenueDetailsChange = (field, value) => { setVenueDetails(prevDetails => ({ ...prevDetails, [dateString]: { ...prevDetails[dateString], [field]: value, }, })); }; const handleSelectFirestoreSuggestion = async (venue) => { setVenueSearchQuery(venue.displayName); setVenueSearchResults([]); try { // Update the venue details with the fetched data setVenueDetails((prevDetails) => ({ ...prevDetails, [dateString]: { id: venue.id || '', name: venue.name || '', displayName: venue.displayName || '', formattedAddress: venue.formattedAddress || '', shortFormattedAddress: venue.shortFormattedAddress || '', adrFormatAddress: venue.adrFormatAddress || '', location: venue.location || null, plusCode: venue.plusCode || '', types: venue.types || [], viewport: venue.viewport || null, accessibilityOptions: venue.accessibilityOptions || {}, businessStatus: venue.businessStatus || '', googleMapsUri: venue.googleMapsUri || '', iconBackgroundColor: venue.iconBackgroundColor || '', iconMaskBaseUri: venue.iconMaskBaseUri || '', primaryType: venue.primaryType || '', primaryTypeDisplayName: venue.primaryTypeDisplayName || '', subDestinations: venue.subDestinations ? venue.subDestinations.map(destination => destination.name).join(', ') : '', utcOffsetMinutes: venue.utcOffsetMinutes || '', currentOpeningHours: venue.currentOpeningHours || '', currentSecondaryOpeningHours: venue.currentSecondaryOpeningHours || '', internationalPhoneNumber: venue.internationalPhoneNumber || '', nationalPhoneNumber: venue.nationalPhoneNumber || '', priceLevel: venue.priceLevel || '', rating: venue.rating || '', regularOpeningHours: venue.regularOpeningHours || '', regularSecondaryOpeningHours: venue.regularSecondaryOpeningHours || '', userRatingCount: venue.userRatingCount || '', websiteUri: venue.websiteUri || '', placeCoverPhotoUrl: venue.placeCoverPhotoUrl || '', }, })); } catch (error) { console.error('Error fetching venue details:', error); Alert.alert('Error', 'Failed to fetch venue details.'); } }; const handleSelectSuggestion = async (suggestion) => { setSearchQuery(suggestion.placePrediction.text.text); setSuggestions([]); try { const response = await fetch( `https://places.googleapis.com/v1/places/${suggestion.placePrediction.placeId}?sessionToken=${sessionToken}`, { method: 'GET', headers: {'Content-Type': 'application/json','X-Goog-Api-Key': 'key','X-Goog-FieldMask':'id,name,photos,addressComponents,adrFormatAddress,formattedAddress,location,plusCode,shortFormattedAddress,types,viewport,accessibilityOptions,businessStatus,displayName,googleMapsUri,iconBackgroundColor,iconMaskBaseUri,primaryType,primaryTypeDisplayName,subDestinations,utcOffsetMinutes,currentOpeningHours,currentSecondaryOpeningHours,internationalPhoneNumber,nationalPhoneNumber,priceLevel,rating,regularOpeningHours,regularSecondaryOpeningHours,userRatingCount,websiteUri', }, } ); const data = await response.json(); console.log('Place Details:', data); if (data.error) { console.error('Error fetching place details:', data.error); Alert.alert('Error', 'Failed to fetch place details.'); return; } const fetchPhotoUrl = async (placeId, photoName) => { const apiKey = 'key'; // Replace with your actual API key const maxWidth = 400; // Specify your desired photo width const maxHeight = 400; // Specify your desired photo height try { console.log('Fetching photo URL for photoName:', photoName); const requestUrl = `https://places.googleapis.com/v1/${photoName}/media?maxWidthPx=400&maxHeightPx=400&key=${apiKey}`; console.log('Request URL:', requestUrl); const response = await fetch(requestUrl, { method: 'GET', headers: {'Content-Type': 'application/json', }, }); console.log('Response status:', response.status); console.log('Response headers:', response.headers); if (response.ok) { const contentType = response.headers.get('Content-Type'); console.log('Content-Type:', contentType); if (contentType.includes('application/json')) { const data = await response.json(); console.log('Response data:', data); if (data.error) { console.error('API error:', data.error); return ''; } return data.photoUri || ''; } else { console.log('Response URL:', response.url); return response.url; } } else { console.error('Error fetching photo:', response.status); const errorData = await response.json(); console.error('Error data:', errorData); return ''; } } catch (error) { console.error('Error fetching photo:', error); return ''; }};const photoName = data.photos && data.photos.length > 0 ? data.photos[0].name : '';console.log('Extracted photoName:', photoName);const placeCoverPhotoUrl = photoName ? await fetchPhotoUrl(data.id, photoName) : ''; // Update the venue details with the fetched data setVenueDetails((prevDetails) => ({ ...prevDetails, [dateString]: { id: data.id || '', name: data.name || '', displayName: data.displayName?.text || '', formattedAddress: data.formattedAddress || '', shortFormattedAddress: data.shortFormattedAddress || '', adrFormatAddress: data.adrFormatAddress || '', location: data.location ? { latitude: data.location.latitude, longitude: data.location.longitude } : null, plusCode: data.plusCode?.globalCode || '', types: data.types || [], viewport: data.viewport ? { northeast: { latitude: data.viewport.high?.latitude || '', longitude: data.viewport.high?.longitude || '', }, southwest: { latitude: data.viewport.low?.latitude || '', longitude: data.viewport.low?.longitude || '', }, } : null, accessibilityOptions: data.accessibilityOptions || {}, businessStatus: data.businessStatus || '', googleMapsUri: data.googleMapsUri || '', iconBackgroundColor: data.iconBackgroundColor || '', iconMaskBaseUri: data.iconMaskBaseUri || '', primaryType: data.primaryType || '', primaryTypeDisplayName: data.primaryTypeDisplayName?.text || '', subDestinations: data.subDestinations ? data.subDestinations.map(destination => destination.name).join(', ') : '', utcOffsetMinutes: data.utcOffsetMinutes || '', currentOpeningHours: data.currentOpeningHours?.periods ? data.currentOpeningHours.periods.map( (period) => `${period.openDay} ${period.openTime} - ${period.closeDay} ${period.closeTime}` ).join(', ') : '', currentSecondaryOpeningHours: data.currentSecondaryOpeningHours?.periods ? data.currentSecondaryOpeningHours.periods.map( (period) => `${period.openDay} ${period.openTime} - ${period.closeDay} ${period.closeTime}` ).join(', ') : '', internationalPhoneNumber: data.internationalPhoneNumber || '', nationalPhoneNumber: data.nationalPhoneNumber || '', priceLevel: data.priceLevel || '', rating: data.rating || '', regularOpeningHours: data.regularOpeningHours?.periods ? data.regularOpeningHours.periods.map( (period) => `${period.openDay} ${period.openTime} - ${period.closeDay} ${period.closeTime}` ).join(', ') : '', regularSecondaryOpeningHours: data.regularSecondaryOpeningHours?.periods ? data.regularSecondaryOpeningHours.periods.map( (period) => `${period.openDay} ${period.openTime} - ${period.closeDay} ${period.closeTime}` ).join(', ') : '', userRatingCount: data.userRatingCount || '', websiteUri: data.websiteUri || '', placeCoverPhotoUrl: placeCoverPhotoUrl || '', }, })); } catch (error) { console.error('Error fetching place details:', error); Alert.alert('Error', 'Failed to fetch place details.'); } }; return (<KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : 'height'} style={styles.container}><View key={dateString}><ScrollView contentContainerStyle={styles.scrollViewContent}><View style={styles.venueSearchContainer}><TextInput style={styles.venueSearchInput} placeholder="Search venue in database" value={venueSearchQuery} onChangeText={setVenueSearchQuery} /><TouchableOpacity style={styles.venueSearchButton} onPress={handleVenueSearch}><Text style={styles.venueSearchButtonText}>Search</Text></TouchableOpacity></View> {venueSearchResults.length > 0 ? (<View style={styles.venueSearchResultsContainer}> {venueSearchResults.map(venue => (<TouchableOpacity key={venue.id} style={styles.venueSearchResult} onPress={() => handleSelectFirestoreSuggestion(venue)}><Text>{venue.displayName}</Text></TouchableOpacity> ))}</View> ) : venueSearchQuery ? (<Text style={styles.venueSearchMessage}>Couldn't find venue in our database.</Text> ) : null}<Text style={styles.stepInputTitle}>Search Venue</Text><TextInput style={styles.input} placeholder="Search venue" value={searchQuery} onChangeText={handleSearchVenue}/><ScrollView style={styles.suggestionsContainer}> {suggestions.map((suggestion, index) => (<TouchableOpacity key={index} style={styles.suggestionItem} onPress={() => handleSelectSuggestion(suggestion)}><Text>{suggestion.placePrediction.text.text}</Text></TouchableOpacity> ))}</ScrollView><View key={dateString}><Text style={styles.stepInputTitle}>Selected Venue Details</Text> {venueDetails[dateString] && (<><View style={styles.venueDetailsContainer}><View style={styles.venueImageContainer}> {venueDetails[dateString]?.placeCoverPhotoUrl ? (<Image source={{ uri: venueDetails[dateString]?.placeCoverPhotoUrl }} style={styles.venueImage} /> ) : (<View style={styles.venueImagePlaceholder} /> )}</View><View style={styles.venueTextContainer}><Text style={styles.venueDisplayName}>{venueDetails[dateString]?.displayName}</Text><Text style={styles.venueAddress}>{venueDetails[dateString]?.formattedAddress}</Text></View></View> {venueDetails[dateString]?.location && (<View style={styles.venueMapContainer}><WebView style={styles.venueMap} originWhitelist={['*']} source={{ html: `<iframe width="100%" height="100%" frameborder="0" style="border:0" referrerpolicy="no-referrer-when-downgrade" src="https://www.google.com/maps/embed/v1/place?key=key&q=${encodeURIComponent(venueDetails[dateString]?.displayName)}&zoom=20&maptype=satellite&language=en" maximum-zoom="21" allowfullscreen></iframe> `, }} /></View> )}</> )}</View></ScrollView></View></KeyboardAvoidingView> ); }; return (<View style={styles.container}><Text style={styles.title}>Create Tour</Text><View style={styles.stepsContainer}> {[1, 2, 3, 4, 5].map((step) => (<View key={step} style={[styles.step, currentStep === step && styles.activeStep]} /> ))}</View><ScrollView contentContainerStyle={styles.scrollView}><View style={styles.formContent}> {renderFormStep()}</View></ScrollView><View style={styles.navigation}> {currentStep > 1 && (<TouchableOpacity style={styles.navButton} onPress={handlePrevStep}><Text style={styles.navButtonText}>Previous</Text></TouchableOpacity> )} {currentStep < 5 ? (<TouchableOpacity style={styles.navButton} onPress={handleNextStep}><Text style={styles.navButtonText}>Next</Text></TouchableOpacity> ) : (<TouchableOpacity style={styles.navButton} onPress={handleSubmit}><Text style={styles.navButtonText}>Submit</Text></TouchableOpacity> )}</View> {isSubmitting && (<View style={styles.loadingContainer}><ActivityIndicator size="large" color="#E2384D" /><Text style={styles.loadingText}>Submitting...</Text></View>)}</View> ); }; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'flex-start', paddingHorizontal: 20, width: '100%', }, // more style codeexport default CreateTour;
I have tried using useCallback
, usememo
, and debounce
but nothing is working. Does someone know how to fix this issue?