I am facing this error even after looking all possible solutions. Previously my code worked perfectly but after I updated react native, I get this warning over and over again Even when I comment out most the code including "useEffect" hook
Could anyone help me so I can get out of this. I am really stuck. I almost post the whole code. The error is "Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn’t have a dependency array, or one of the dependencies changes on every render"
import {
View,
ScrollView,
StyleSheet,
TouchableOpacity,
Alert,
PermissionsAndroid,
Platform,
Image,
Switch,
} from 'react-native';
import CheckBox from '@react-native-community/checkbox';
import {Text, Button, Input} from 'react-native-elements';
import BackHeader from '../../../Navigation/BackHeader';
import {
createRental,
getRentalFrequencies,
} from '../../../services/RentalService';
import {launchCamera, launchImageLibrary} from 'react-native-image-picker';
import Icon from '../../../consts/Icon';
import Geolocation from '@react-native-community/geolocation';
import COLORS from '../../../consts/colors';
import {useNavigation} from '@react-navigation/native';
import {useDispatch, useSelector} from 'react-redux';
import NetInfo from '@react-native-community/netinfo';
import Spinner from 'react-native-loading-spinner-overlay';
import BottomSheet from '../../../components/BottomSheet';
import imagePath from '../../../consts/imagePath';
import {fetchMyRentals} from '../../../store/myRentalsSlice';
import {Picker} from '@react-native-picker/picker';
const AddRentalScreen = () => {
const navigation = useNavigation();
const owner = useSelector(state => state.user.user);
const [step, setStep] = useState(1);
const [rentalFreqs, setRentalFreqs] = useState([]);
const [isCurrentLocation, setIsCurrentLocation] = useState(true);
const [villages, setVillages] = useState([]);
const [selectedVillage, setSelectedVillage] = useState(null);
const [useCurrentLocation, setUseCurrentLocation] = useState(true);
const [nearVillage, setNearVillage] = useState('');
const [isStep1Valid, setIsStep1Valid] = useState(false);
const [isStep2Valid, setIsStep2Valid] = useState(false);
const [isStep3Valid, setIsStep3Valid] = useState(false);
const [isStep4Valid, setIsStep4Valid] = useState(false);
const [isStep5Valid, setIsStep5Valid] = useState(false);
const [loading, setLoading] = useState(false);
const [formData, setFormData] = useState({
titles: '',
currency: 'UGX',
price: '',
nmonths: '',
utilities: '',
frequency: '',
conditions: '',
address: '',
province: '',
village: '',
parking: false,
amenities: '',
toilet: '',
surfaceArea: '',
images: [],
propertyDetails: '',
villageId: '',
latitude: 0.0,
longitude: 0.0,
rentalType: 'RESIDENTIAL',
bedrooms: '',
bedRoomLength: '',
bedRoomWidth: '',
bathrooms: '',
livingrooms: '',
livingroomLength: '',
livingroomWidth: '',
hasBalcony: false,
hasGarden: false,
hasGarage: false,
hasRestRoom: false,
hasSwimmingPool: false,
hasSecuritySystem: false,
hasMultipleRooms: false,
noOfRooms: '',
noOfToilets: '',
isSelfContained: false,
isKitchenIn: false,
hasSecurityPersonnel: false,
enclosedInGate: false,
dogPresent: false,
privateOffices: '',
conferenceRooms: '',
workstations: '',
hasReceptionArea: false,
hasKitchenette: false,
hasBreakRoom: false,
hasHighSpeedInternet: false,
has24x7Security: false,
hasFitnessCenter: false,
hasCafeteria: false,
hasStorageRoom: false,
hasDisplayWindow: false,
nearbyAmenities: '',
footTraffic: 'LOW',
publicTransportAccessibility: '',
competitionLevel: 'LOW',
safetyAndSecurity: '',
zoning: '',
proximityToResidentialAreas: '',
proximityToSchools: '',
proximityToParks: '',
proximityToCommunityCenters: '',
proximityToMarkets: '',
trafficPatterns: '',
hasStoreyedStructure: false,
floorNumber: '',
totalLengthInFeet: '',
totalWidthInFeet: '',
});
const bottomSheetRef = useRef();
const watchIdRef = useRef(null);
const dispatch = useDispatch();
const handleSelectRentalType = useCallback(value => {
setFormData({...formData, rentalType: value});
}, []);
const handleFrequencyChange = useCallback(value => {
setFormData({...formData, frequency: value});
}, []);
const handleCurrencySelect = useCallback(value => {
setFormData({...formData, currency: value});
}, []);
const getCurrentLocation = () => {
if (watchIdRef.current) {
Geolocation.clearWatch(watchIdRef.current);
}
watchIdRef.current = Geolocation.getCurrentPosition(
position => {
const {latitude, longitude} = position.coords;
const googleLocation = `${latitude},${longitude}`;
console.log(googleLocation, 'location');
setFormData({...formData, latitude: latitude, longitude: longitude});
},
error => {
console.log(error.message);
},
{enableHighAccuracy: false, timeout: 40000, maximumAge: 10000},
);
};
const validateStep1 = () => {
const {titles, currency, price, nmonths, utilities, frequency} = formData;
return titles && currency && price && nmonths && utilities && frequency;
};
const validateStep2 = () => {
const {address, province} = formData;
return address && province && (isCurrentLocation || selectedVillage);
};
const validateStep3 = () => {
const {
rentalType,
hasMultipleRooms,
bedrooms,
bedRoomLength,
bedRoomWidth,
bathrooms,
livingrooms,
livingroomLength,
livingroomWidth,
privateOffices,
conferenceRooms,
workstations,
totalLengthInFeet,
totalWidthInFeet,
trafficPatterns,
footTraffic,
competitionLevel,
zoning,
hasStoreyedStructure,
floorNumber,
proximityToResidentialAreas,
proximityToSchools,
proximityToParks,
proximityToCommunityCenters,
proximityToMarkets,
} = formData;
const sharedValidations = totalLengthInFeet && totalWidthInFeet;
if (rentalType === 'RESIDENTIAL') {
return (
sharedValidations &&
(hasMultipleRooms
? bedrooms &&
bedRoomLength &&
bedRoomWidth &&
bathrooms &&
livingrooms &&
livingroomLength &&
livingroomWidth
: true)
);
} else if (rentalType === 'OFFICE') {
return (
privateOffices &&
conferenceRooms &&
workstations &&
totalLengthInFeet &&
totalWidthInFeet
);
} else if (rentalType === 'SHOP') {
return (
sharedValidations &&
footTraffic &&
competitionLevel &&
zoning &&
(hasStoreyedStructure ? floorNumber : true) &&
proximityToResidentialAreas &&
proximityToSchools &&
proximityToParks &&
proximityToCommunityCenters &&
proximityToMarkets &&
trafficPatterns
);
}
};
const validateStep4 = () => {
return formData.propertyDetails;
};
const validateStep5 = () => {
return formData.images.length > 2;
};
const validateAllSteps = () => {
const step1Valid = validateStep1();
const step2Valid = validateStep2();
const step3Valid = validateStep3();
const step4Valid = validateStep4();
const step5Valid = validateStep5();
if (isStep1Valid !== step1Valid) setIsStep1Valid(step1Valid);
if (isStep2Valid !== step2Valid) setIsStep2Valid(step2Valid);
if (isStep3Valid !== step3Valid) setIsStep3Valid(step3Valid);
if (isStep4Valid !== step4Valid) setIsStep4Valid(step4Valid);
if (isStep5Valid !== step5Valid) setIsStep5Valid(step5Valid);
return {
isStep1Valid,
isStep2Valid,
isStep3Valid,
isStep4Valid,
isStep5Valid,
};
};
const initialize = async () => {
if (Platform.OS === 'android') {
await requestCameraPermission();
await requestStoragePermission();
}
};
if (!formData.images.length) {
Alert.alert('Please select at least one image');
return;
}
const {roles, ...ownerWithoutRoles} = owner;
const propertyData = {
rentalType: formData.rentalType,
title: formData.titles,
owner: formData.ownerWithoutRoles,
currency: formData.currency,
propertyDetails: formData.propertyDetails,
pricePerMonth: formData.price,
initialPayMonth: formData.nmonths,
rentFrequency: formData.frequency,
utilitiesToPay: formData.utilities,
latitude: isCurrentLocation ? formData.latitude : 0.0,
longitude: isCurrentLocation ? formData.longitude : 0.0,
addressName: formData.address,
postalCode: formData.postal_code,
province: formData.province,
isParkingAvailable: formData.parking,
totalLengthInFeet: formData.totalLengthInFeet,
totalWidthInFeet: formData.totalWidthInFeet,
};
if (propertyData.rentalType === 'RESIDENTIAL') {
Object.assign(propertyData, {
noOfToilets: formData.toilet,
noOfRooms: formData.bedrooms,
isKitchenIn: formData.isKitchenIn,
isSelfContained: formData.isSelfContained,
conditionsOfStay: formData.conditions,
hasMultipleRooms: formData.hasMultipleRooms,
isSelfContained: formData.isSelfContained,
hasBalcony: formData.hasBalcony,
hasGarden: formData.hasGarden,
hasSwimmingPool: formData.hasSwimmingPool,
hasGarage: formData.hasGarage,
hasSecuritySystem: formData.hasSecuritySystem,
isKitchenIn: formData.isKitchenIn,
bedrooms: formData.bedrooms,
bedRoomLength: formData.bedRoomLength,
bedRoomWidth: formData.bedRoomWidth,
livingrooms: formData.livingrooms,
livingroomLength: formData.livingroomLength,
livingroomWidth: formData.livingroomWidth,
bathrooms: formData.bathrooms,
});
} else if (propertyData.rentalType === 'SHOP') {
Object.assign(propertyData, {
hasStorageRoom: formData.hasStorageRoom,
hasStoreyedStructure: formData.hasStoreyedStructure,
hasDisplayWindow: formData.hasDisplayWindow,
hasRestRoom: formData.hasRestRoom,
footTraffic: formData.footTraffic,
zoning: formData.zoning,
competitionLevel: formData.competitionLevel,
trafficPatterns: formData.trafficPatterns,
floorNumber: formData.floorNumber,
proximityToResidentialAreas: formData.proximityToResidentialAreas,
proximityToSchools: formData.proximityToSchools,
proximityToParks: formData.proximityToParks,
proximityToCommunityCenters: formData.proximityToCommunityCenters,
proximityToMarkets: formData.proximityToMarkets,
});
} else if (propertyData.rentalType === 'OFFICE') {
Object.assign(propertyData, {
privateOffices: formData.privateOffices,
conferenceRooms: formData.conferenceRooms,
workstations: formData.workstations,
hasReceptionArea: formData.hasReceptionArea,
hasKitchenette: formData.hasKitchenette,
hasBreakRoom: formData.hasBreakRoom,
hasHighSpeedInternet: formData.hasHighSpeedInternet,
has24x7Security: formData.has24x7Security,
hasFitnessCenter: formData.hasFitnessCenter,
hasCafeteria: formData.hasCafeteria,
});
}
const fileObjects = formData.images.map(image => image.file);
const rentalData = {
property: propertyData,
files: fileObjects,
};
setLoading(true);
const resp = await createRental(rentalData);
if (resp?.status === 202) {
setLoading(false);
dispatch(fetchMyRentals(owner.id));
bottomSheetRef.current.open();
} else if (resp?.status === 400) {
setLoading(false);
Alert.alert('Bad Request!');
} else {
setLoading(false);
Alert.alert('Error Registering rental.' + resp._response);
}
};
const handleImageUpload = imageNumber => {
Alert.alert(
'Choose Image Source',
'Select the source for your image',
[
{
text: 'Camera',
onPress: () => launchCameraAction(imageNumber),
},
{
text: 'Gallery',
onPress: () => launchImageLibraryAction(imageNumber),
},
],
{cancelable: true},
);
};
const handleImageRemoval = imageNumber => {
const filteredImages = formData.images.filter(
image => image.id !== imageNumber,
);
setFieldValue('images', filteredImages);
};
const onSetCurrentLocation = () => {
setIsCurrentLocation(!isCurrentLocation);
setUseCurrentLocation(!useCurrentLocation);
};
const launchCameraAction = async imageNumber => {
const options = {
mediaType: 'photo',
maxWidth: 800,
maxHeight: 600,
quality: 1,
includeBase64: true,
};
launchCamera(options, response => {
if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else {
console.log('response', response);
const fileExtension = response.assets?.[0].fileName.split('.').pop();
const imageData = {
uri: response.assets?.[0].uri,
base64: response.assets?.[0].base64,
type: response.assets?.[0].type,
name: `${imageNumber}.${fileExtension}`,
};
setFormData(prevFormData => ({
...prevFormData,
images: [...prevFormData.images, {id: imageNumber, file: imageData}],
}));
}
});
};
const launchImageLibraryAction = async imageNumber => {
const options = {
mediaType: 'photo',
maxWidth: 800,
maxHeight: 600,
quality: 1,
includeBase64: true,
};
launchImageLibrary(options, response => {
if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.errorCode) {
console.log('ImagePicker Error: ', response.errorMessage);
} else {
const fileExtension = response.assets?.[0].fileName.split('.').pop();
const imageData = {
uri: response.assets?.[0].uri,
base64: response.assets?.[0].base64,
type: response.assets?.[0].type,
name: `${imageNumber}.${fileExtension}`,
};
setFormData(prevFormData => ({
...prevFormData,
images: [...prevFormData.images, {id: imageNumber, file: imageData}],
}));
}
});
};
const requestCameraPermission = async () => {
try {
const isGranted = await PermissionsAndroid.check(
PermissionsAndroid.PERMISSIONS.CAMERA,
);
if (!isGranted) {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.CAMERA,
{
title: 'Camera Permission',
message: 'This app requires access to your camera.',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log('Camera permission granted');
} else {
console.log('Camera permission denied');
}
}
} catch (err) {
console.warn(err);
}
};
const requestStoragePermission = useCallback(async () => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
{
title: 'Storage Permission',
message: 'This app requires access to your storage.',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log('Storage permission granted');
} else {
console.log('Storage permission denied');
}
} catch (err) {
console.warn(err);
}
}, []);
const step1 = useMemo(() => {
return (
<>
<Text h4>Basic Information</Text>
<View style={{margin: 12}}>
<Text style={styles.dropDownLabel}>Rental Type</Text>
<View
style={{
borderBottomWidth: 1,
borderBottomColor: '#ccc',
paddingBottom: 4,
}}>
<Picker
mode="dropdown"
selectedValue={formData.rentalType}
onValueChange={val => handleSelectRentalType(val)}>
<Picker.Item label="Residential" value="RESIDENTIAL" />
<Picker.Item label="Business" value="SHOP" />
<Picker.Item label="Office" value="OFFICE" />
</Picker>
</View>
</View>
<Input
label="Title"
placeholder="ex: Modern Apartment Downtown..."
value={formData.titles}
onChangeText={text => setFormData({...formData, titles: text})}
/>
<View style={{margin: 12}}>
<Text style={styles.dropDownLabel}>Currency</Text>
<View
style={{
borderBottomWidth: 1,
borderBottomColor: '#ccc',
paddingBottom: 4,
}}>
<Picker
mode="dropdown"
selectedValue={formData.currency}
onValueChange={val => handleCurrencySelect(val)}>
<Picker.Item label="UGX" value="UGX" />
<Picker.Item label="USD" value="USD" />
<Picker.Item label="KSH" value="KSH" />
<Picker.Item label="TSH" value="TSH" />
<Picker.Item label="CDF" value="CDF" />
</Picker>
</View>
</View>
<Input
label="Price"
placeholder="Ex: 500,000"
value={formData.price}
keyboardType="numeric"
onChangeText={text => setFormData({...formData, price: text})}
/>
<Input
label="Initial payment (New tenant pay at start)"
placeholder="Ex: 3 (Ex: Meaning three Months)"
value={formData.nmonths}
keyboardType="numeric"
onChangeText={text => setFormData({...formData, nmonths: text})}
/>
<Input
label="Utilities to pay (Client's seperate utilities)"
placeholder="Ex: Electricity"
value={formData.utilities}
onChangeText={text => setFormData({...formData, title: text})}
/>
<View style={{margin: 12}}>
<Text style={styles.dropDownLabel}>
Rent Frequency (Rent paid every ...)
</Text>
<View
style={{
borderBottomWidth: 1,
borderBottomColor: '#ccc',
paddingBottom: 4,
}}>
<Picker
mode="dropdown"
selectedValue={formData.frequency}
onValueChange={val => handleFrequencyChange(val)}>
{rentalFreqs.map((freq, index) => (
<Picker.Item key={index} label={freq.name} value={freq.name} />
))}
</Picker>
</View>
</View>
<Input
label="Conditions of stay"
placeholder="Any brief rules"
multiline={true}
value={formData.conditions}
onChangeText={text => setFormData({...formData, title: text})}
/>
</>
);
}, [rentalFreqs]);
const step2 = useMemo(() => {
return (
<>
<Text h4>Address Information</Text>
<Input
label="Address"
placeholder="Address of the rental"
value={formData.address}
onChangeText={text => setFormData({...formData, address: text})}
/>
<Input
label="District"
placeholder="District of the rental"
value={formData.province}
onChangeText={text => setFormData({...formData, province: text})}
/>
<View style={{width: '100%', alignContent: 'flex-start'}}>
<View
style={{
width: '100%',
flexDirection: 'row',
alignItems: 'center',
marginBottom: 10,
}}>
<CheckBox
value={isCurrentLocation}
onValueChange={onSetCurrentLocation}
/>
<Text>Use my current Location for rental location?</Text>
</View>
</View>
</>
);
}, []);
return (
<View style={styles.container}>
<BackHeader navigation={navigation} title={`Add Rental`} />
<ScrollView style={styles.scrollView}>
<View style={styles.formContainer}>
{step === 1 && step1}
{step === 2 && step2}
{step === 3 && step3}
{step === 4 && step4}
{step === 5 && step5}
{step === 6 && step6}
</View>
</ScrollView>
<View
style={[
styles.navigationView,
{
flexDirection: step < 2 ? 'column' : 'row',
justifyContent: step < 2 ? 'flex-end' : 'space-between',
},
]}>
<Spinner
visible={loading}
textContent={'submitting...'}
textStyle={styles.spinnerTextStyle}
/>
{step > 1 && <Button title="Previous" onPress={handlePreviousStep} />}
<Button
title={step === 6 ? 'Submit' : 'Next'}
onPress={step === 6 ? handleSubmitForm : handleNextStep}
disabled={
(step === 1 && !isStep1Valid) ||
(step === 2 && !isStep2Valid) ||
(step === 3 && !isStep3Valid) ||
(step === 4 && !isStep4Valid) ||
(step === 5 && !isStep5Valid)
}
buttonStyle={
step === 6
? styles.submitButton
: step < 2
? styles.largeButton
: styles.defaultButton
}
titleStyle={step < 2 ? styles.buttonTitle : null}
/>
</View>
<BottomSheet bottomSheetRef={bottomSheetRef}>
<View style={styles.sheetContent}>
<Image
source={imagePath.checkedSuccess}
style={styles.checkedImage}
/>
<Text style={styles.successText}>
Rental successfully Registered!
</Text>
<TouchableOpacity
style={styles.okButton}
onPress={() => navigation.goBack()}>
<Text style={styles.okButtonText}>OK</Text>
</TouchableOpacity>
</View>
</BottomSheet>
</View>
);
};
export default React.memo(AddRentalScreen);
Where could I have gone wrong? Please help me
2
Answers
After cloning your project in Visual Studio, I found two major syntax mistakes:
There was an extra curly brace on line 353, which caused the
AddRentalScreen component to end prematurely. As a result, the logic
after line 353 was not executed correctly.
All of the createRental logic was written outside of a function, in
the component scope, and it included an await statement. The issue
with this is that await can only be used inside an async function.
This could be the reason why you’re encountering issues.
To resolve these errors:
response to an action, such as a button press.
create rental possible function:
Probably there is one issue with the useMemo function calls.
Here is the fixed code for step1
And this is for step2