I'm encountering an issue related to asynchronous behavior in my React Native functional component that involves the use of AsyncStorage. I have a custom component for AsyncStorage operations, and I've also tried using AsyncStorage directly, but the problem persists.
Code:
I have a React Native functional component named EntryScreen, and I'm using AsyncStorage for handling user data. The component involves various asynchronous operations, including data retrieval and synchronization with Firebase.
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable @typescript-eslint/no-shadow */
/* eslint-disable react-native/no-inline-styles */
import React, {useEffect, useState} from 'react';
import {
Text,
TouchableOpacity,
SafeAreaView,
Modal,
View,
TextInput,
FlatList,
Alert,
} from 'react-native';
import AntDesign from 'react-native-vector-icons/AntDesign';
import DatePicker from 'react-native-date-picker';
import {Image, Switch} from '@rneui/base';
import {ScheduleNotification} from '../../utils/Notification/notification';
import {Dropdown} from 'react-native-element-dropdown';
import PushNotification from 'react-native-push-notification';
import styles from './styles';
import NotificationChannels from '../../utils/Notification/NotificationChannels';
import State from '../../../Store';
import AsyncStorage from '@react-native-async-storage/async-storage';
import CreateUserDB from '../../utils/db/CreateUserDB';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
import database from '@react-native-firebase/database';
const EntryScreen = ({navigation}: any) => {
// user var which comes from Async Storage
const [user, setUser] = useState<any>();
const modal = State.useState(state => state.alarmModal);
const [date, setDate] = useState<number>(Date.now());
const snooze = State.useState(state => state.snooze);
const [alarmLabel, setAlarmLabel] = useState<string>('');
const [data, setData] = useState<any>([]);
const [updateId, setUpdateId] = useState<number>(0);
const [channel, setChannel] = useState<string>('');
const db = CreateUserDB(user?.username);
useEffect(() => {
initializeData();
NotificationChannels.alarms_carpark();
NotificationChannels.alarms_sneez();
NotificationChannels.alarms_synth();
}, []);
useEffect(() => {
db.createTable();
}, []);
useEffect(() => {
db.getData(dataArr => setData(dataArr));
}, [data]);
useEffect(() => {
PushNotification.localNotification({
title: 'hello',
message: 'hello',
channelId: 'alarms_sneez',
});
}, []);
useEffect(() => {
const autoSyncFirebase = async () => {
const userRef = database().ref(
user?.authType === 'email' ? `/${user.username}` : `/${user.id}`,
);
await Promise.all(
data.map(async (element: any) => {
await db.deleteElement(element.id);
}),
);
const snapshot = await userRef.once('value');
const fbData = snapshot.val().data;
await Promise.all(
fbData.map(async (element: any) => {
await db.setData(
element.timestamp,
element.title,
element.isNotificationOn,
element.isSnooze,
);
}),
);
};
autoSyncFirebase();
}, []);
const initializeData = async () => {
const retrievedUser = await AsyncStorage.getItem('user');
if (retrievedUser) {
setUser(JSON.parse(retrievedUser));
}
};
const handleOnListPress = (item: any) => {
State.update(state => {
state.alarmModal = true;
});
setUpdateId(item.id);
setDate(item.timestamp);
setAlarmLabel(item.title);
};
const onCancelPress = () => {
State.update(state => {
state.alarmModal = false;
});
setDate(Date.now());
setUpdateId(0);
State.update(state => {
state.snooze = false;
});
};
const onSavePress = () => {
const isNewAlarm = updateId === 0;
isNewAlarm
? db.setData(date, alarmLabel, true, snooze)
: db.updateAlarm(date, alarmLabel, snooze, updateId);
setDate(Date.now());
State.update(state => {
state.alarmModal = false;
state.snooze = false;
});
ScheduleNotification(new Date(date), alarmLabel, snooze, channel);
};
const onDeleteAlarmPress = () => {
db.deleteElement(updateId);
State.update(state => {
state.alarmModal = false;
});
setUpdateId(0);
};
const firebaseSync = async () => {
const userRef = await database().ref(
user.authType === 'email' ? `/${user.username}` : `/${user.id}`,
);
if (data > 0) {
userRef.update({data: data});
} else {
function syncFromFirebase() {
data.forEach((element: any) => {
db.deleteElement(element.id);
});
userRef.once('value').then(snapshot => {
const fbData = snapshot.val().data;
fbData.forEach((element: any) => {
db.setData(
element.timestamp,
element.title,
element.isNotificationOn,
element.isSnooze,
);
});
});
}
function syncData() {
userRef.update({data: data});
}
Alert.alert('WARNING', 'DO YOU WANNA SYNC EMPTY DATA?', [
{text: 'get synced data', onPress: syncFromFirebase},
{text: 'sync data', onPress: syncData},
]);
}
};
const renderItem = ({item}: any) => {
const dateString = new Date(item.timestamp).toLocaleTimeString([], {
hour12: true,
hour: '2-digit',
minute: '2-digit',
});
const handleSwitchToggle = (value: boolean, item: any) => {
if (value) {
db.updateNotificationValue(true, item.id);
ScheduleNotification(
new Date(item.timestamp),
item.title,
item.isSnooze,
channel,
);
} else {
db.updateNotificationValue(false, item.id);
PushNotification.getScheduledLocalNotifications(list => {
list.forEach(notification => {
const itemTimestamp = new Date(item.timestamp).toLocaleTimeString(
[],
{hour: '2-digit', minute: '2-digit', hour12: true},
);
const notificationTimestamp = notification.date.toLocaleTimeString(
[],
{hour: '2-digit', minute: '2-digit', hour12: true},
);
if (itemTimestamp === notificationTimestamp) {
PushNotification.cancelLocalNotification(notification.id);
}
});
});
}
};
return (
<TouchableOpacity
onPress={() => handleOnListPress(item)}
style={[styles.listViewStyles]}>
<View>
<Text style={styles.mainDateString}>{dateString}</Text>
<Text>{item.title}, Every Wednesday</Text>
</View>
<Switch
value={!!item.isNotificationOn}
onValueChange={value => handleSwitchToggle(value, item)}
/>
</TouchableOpacity>
);
};
return (
<SafeAreaView style={styles.mainContainer}>
<View style={styles.topBarInnerContainer}>
<MaterialIcons
name="sync"
size={30}
color={'white'}
onPress={firebaseSync}
style={styles.syncBtn}
/>
<Image
source={
user?.photo
? {uri: user?.photo}
: require('../../assets/images/google.png')
}
style={styles.profileImg}
resizeMode="contain"
onPress={() => {
navigation.navigate('profile');
}}
/>
</View>
<View style={styles.topBarContainer}>
<Text style={styles.headerFont}>Alarms</Text>
<TouchableOpacity
style={styles.plusBtn}
onPress={() =>
State.update(state => {
state.alarmModal = true;
})
}>
<AntDesign name="plus" size={40} color={'orange'} />
</TouchableOpacity>
</View>
<FlatList
keyExtractor={item => item.id.toString()}
renderItem={renderItem}
data={data}
/>
<Modal visible={modal} transparent={false} animationType="slide">
<SafeAreaView style={styles.modalContainer}>
<View style={styles.modalTopBar}>
<TouchableOpacity
style={styles.modalTopBtn}
onPress={onCancelPress}>
<Text style={styles.modalTopBtnText}>Cancel</Text>
</TouchableOpacity>
<Text style={{fontSize: 20}}>Set Timer</Text>
<TouchableOpacity style={styles.modalTopBtn} onPress={onSavePress}>
<Text style={styles.modalTopBtnText}>
{updateId === 0 ? 'Save' : 'Update'}
</Text>
</TouchableOpacity>
</View>
<View style={styles.modalInnerContainer}>
<DatePicker
date={new Date(date)}
onDateChange={date => setDate(date.getTime())}
mode="time"
style={styles.datePickerStyles}
is24hourSource="locale"
/>
<View style={styles.bottomContainerMain}>
<View style={styles.modalBottomContainerItem}>
<Text style={styles.modalBottomContainerLabel}>Repeat</Text>
<TouchableOpacity style={styles.neverBtn}>
<Text style={{fontSize: 18}}>Never</Text>
<AntDesign name="right" size={20} />
</TouchableOpacity>
</View>
<View style={styles.modalBottomContainerItem}>
<Text style={styles.modalBottomContainerLabel}>Label</Text>
<TextInput
placeholder="Hello there"
value={alarmLabel}
onChangeText={val => setAlarmLabel(val)}
/>
</View>
<View style={styles.modalBottomContainerItem}>
<Text style={[styles.modalBottomContainerLabel, {flex: 2}]}>
Sound
</Text>
<Dropdown
labelField={'label'}
data={[
{value: 1, label: 'alarm_carpark'},
{value: 2, label: 'alarm_sneez'},
{value: 3, label: 'alarm_synth'},
]}
valueField={'value'}
placeholder={channel}
value={channel}
onChange={item => setChannel(item.label)}
style={styles.dropDownStyles}
/>
</View>
<View style={styles.modalBottomContainerItem}>
<Text style={styles.modalBottomContainerLabel}>Snooze</Text>
<Switch
color="green"
value={snooze}
onValueChange={() => {
State.update(state => {
state.snooze = !state.snooze;
});
}}
/>
</View>
</View>
{updateId !== 0 && (
<TouchableOpacity
onPress={onDeleteAlarmPress}
style={styles.deleteAlarmBtn}>
<Text style={styles.deleteAlarmBtnText}>Delete Alarm</Text>
</TouchableOpacity>
)}
</View>
</SafeAreaView>
</Modal>
<TouchableOpacity
style={styles.logOutBtn}
onPress={async () => {
await AsyncStorage.removeItem('user')
.then(() => {
data.forEach((element: any) => db.deleteElement(element.id));
console.log('success');
navigation.navigate('signup');
setData(undefined);
})
.catch(error => console.log(error));
}}>
<Text style={styles.logOutBtnText}>Log out</Text>
</TouchableOpacity>
</SafeAreaView>
);
};
export default EntryScreen;
export const getAsyncVar = async (key: string) => {
try {
const response = await AsyncStorage.getItem(key);
if (typeof response === 'object') {
if (response) {
return JSON.parse(response);
}
} else {
return response;
}
} catch (error) {
console.log(error);
}
return null;
};
export const getAllAsyncVars = async () => {
try {
const response = await AsyncStorage.getAllKeys();
if (response) {
return response;
}
} catch (error) {
console.log(error);
}
return null;
};
Issues:
- I'm encountering the error "Possible unhandled promise rejection: cannot read property 'id' of undefined." in the
renderItemfunction. - There seems to be an issue with the asynchronous behavior of the
autoSyncFirebasefunction.
Specific Questions:
- How can I handle the "Possible unhandled promise rejection" error in the
renderItemfunction? - Are there any improvements needed in the
autoSyncFirebasefunction to ensure proper asynchronous behavior?
I appreciate any guidance or suggestions on resolving these issues. Thank you!