JIT
handling logout and navigation in react native app
import React, {useState, useEffect, useCallback, useRef} from 'react'; import { CommonActions, NavigationContainer, useNavigationContainerRef, } from '@react-navigation/native'; import {createNativeStackNavigator} from '@react-navigation/native-stack'; import { ActivityIndicator, View, StyleSheet, Alert, Text, Dimensions, } from 'react-native'; import Toast from 'react-native-toast-message'; import {LogBox} from 'react-native'; // Contexts and Utils import AsyncStorageManager from '../utils/AsyncStorageManager'; // Adjust path if needed import {UserProvider, useUser} from '../context/UserContext'; // Adjust path if needed import { MqttProvider, useMqtt, MqttConnectionState, } from '../context/MqttContext'; // Adjust path if needed // Screens and Navigators import LoginScreen from '../screens/LoginScreen'; // Adjust path if needed import MainTabNavigator from './MainTabNavigator'; // Adjust path if needed import CommunityItemDetailScreen from '../screens/CommunityItemDetailScreen'; // <<< Import the detail screen import GalleryScreen from '../screens/GalleryScreen'; import FrontCameraScreen from '../screens/FrontCameraScreen'; import DeviceScreen from '../screens/DeviceScreen'; import ZoneScreen from '../screens/ZoneScreen'; import AlarmsScreen from '../screens/AlarmsScreen'; import DeviceItemScreen from '../screens/DeviceItemScreen'; import GiveAccessScreen from '../screens/GiveAccessScreen'; import ViewAccessScreen from '../screens/ViewAccessScreen'; // Notification Service import NotificationService, { ACTION_ACCEPT, ACTION_DECLINE, ACTION_SILENCE, } from '../services/NotificationService'; // Adjust path if needed import CCTVScreen from '../screens/CameraTabs/CCTVScreen'; import ZoneItemScreen from '../screens/ZoneItemScreen'; import AmenityScreen from '../screens/AmenityScreen'; import EventScreen from '../screens/EventScreen'; import CommunityScreen from '../screens/CommunityScreen'; import AmenityDetailsScreen from '../screens/AmenityDetailsScreen'; import EventDetailsScreen from '../screens/EventDetailsScreen'; import BookingScreen from '../screens/BookingTabs/BookingScreen'; const Stack = createNativeStackNavigator(); const {height: screenHeight} = Dimensions.get('window'); // --- Notification Handling Logic (Keep as before) --- const handleNotificationOpen = (navigationRef, data, action) => { console.log('Handling Notification Open/Action:', {data, action}); if (!data) { console.warn('No data found in notification to handle.'); return; } const targetScreen = data.screen; const notificationId = data.localNotificationId; // Assuming you pass this in userInfo // Example action handling (customize as needed) if (action) { console.log( `Action '${action}' taken on notification ID: ${notificationId}.`, ); // Handle specific actions like ACCEPT, DECLINE, SILENCE if (action === ACTION_ACCEPT) { console.log(`Handling ACCEPT action.`); // Navigate or perform accept logic if (targetScreen && navigationRef.isReady()) { navigationRef.navigate(targetScreen, { notificationData: data, actionTaken: action, }); } } else if (action === ACTION_DECLINE) { console.log(`Handling DECLINE action.`); // Perform decline logic } else if (action === ACTION_SILENCE) { console.log(`Handling SILENCE action.`); // Perform silence logic } // Optionally cancel the notification after handling the action // notificationService.current?.cancelNotificationById(notificationId); // Need access to service instance } else { // --- Notification Tapped --- console.log(`Handling TAP action for notification ID: ${notificationId}`); if (targetScreen && navigationRef.isReady()) { console.log(`Navigating to screen: ${targetScreen}`); // Ensure navigation happens correctly, potentially checking if already on the screen // or using reset/push depending on desired behavior. // Check if the target screen is the detail screen if (targetScreen === 'CommunityItemDetail') { // Ensure data contains the necessary 'item' for the detail screen if (data.item) { navigationRef.navigate(targetScreen, {item: data.item}); } else { console.warn( 'Notification tapped for CommunityItemDetail but missing item data.', ); // Optionally navigate to a default state or show an error } } else { // Handle navigation for other screens navigationRef.navigate(targetScreen, {notificationData: data}); } } else if (!targetScreen) { console.log('No target screen specified in notification data.'); } else { console.warn('Navigation not ready when notification was opened.'); // Optionally queue the navigation action until ready } } }; // --- Main Content Component --- const AppNavigationContent = () => { const [isLoading, setIsLoading] = useState(true); const [isLoggedIn, setIsLoggedIn] = useState(false); const [fcmToken, setFcmToken] = useState(null); const {setUserDetails, clearUserDetails, updateUserDetails} = useUser(); // Added updateUserDetails const { connect: connectMqtt, disconnect: disconnectMqtt, connectionState: mqttState, } = useMqtt(); const shownConnectedToastRef = useRef(false); const navigationRef = useNavigationContainerRef(); const notificationService = useRef(null); const [notificationInitialized, setNotificationInitialized] = useState(false); // Initialize Notification Service Ref (runs only once) useEffect(() => { LogBox.ignoreAllLogs(true); const onRegisterToken = token => { console.log('AppNavigator received FCM Token:', token); setFcmToken(token); // Store the token // TODO: Send the token to your backend server here if needed // if (token) { sendTokenToServer(token); } }; const onNotificationOpened = (data, action) => { // Ensure navigationRef is available and ready if needed handleNotificationOpen(navigationRef, data, action); }; console.log('AppNavigator: Creating NotificationService instance...'); // Pass the callbacks when creating the instance notificationService.current = new NotificationService( onNotificationOpened, onRegisterToken, ); // Don't initialize immediately here, wait for login status }, []); // Empty dependency array ensures this runs only once // Effect to initialize notifications AFTER login status is confirmed useEffect(() => { // Only initialize if logged in AND not already initialized if (isLoggedIn && notificationService.current && !notificationInitialized) { console.log( 'User logged in, initializing Notification Service (will request permissions)...', ); // Call initialize() which now includes requesting permissions notificationService.current .initialize() .then(() => { console.log('Notification Service initialization attempt complete.'); setNotificationInitialized(true); // Mark as initialized (or attempted) }) .catch(error => { console.error( 'Error during Notification Service initialization:', error, ); setNotificationInitialized(true); // Mark as initialized even on error to prevent retries }); } else if (!isLoggedIn && notificationInitialized) { // Optional: Reset initialization status if user logs out console.log( 'User logged out, resetting notification initialization status.', ); setNotificationInitialized(false); } }, [isLoggedIn, notificationInitialized]); // Depend on login status and initialization status // MQTT Connection Toast (Keep as before) useEffect(() => { if (mqttState === MqttConnectionState.CONNECTED) { if (!shownConnectedToastRef.current) { Toast.show({ type: 'success', text1: 'MQTT Status', text2: 'Successfully connected! 👋', position: 'bottom', visibilityTime: 3000, }); shownConnectedToastRef.current = true; } } else if ( mqttState !== MqttConnectionState.CONNECTING && mqttState !== MqttConnectionState.RECONNECTING ) { if (shownConnectedToastRef.current) { shownConnectedToastRef.current = false; } } }, [mqttState]); useEffect(() => { let isMounted = true; const checkLoginStatus = async () => { setIsLoading(true); // Start loading indicator try { const storedUserData = await AsyncStorageManager.getItem('userData'); console.log('isMounted', isMounted); console.log('storedUserData', storedUserData); if (isMounted) { if (storedUserData && storedUserData.token) { // Check only for token existence console.log('storedUserData after ismount', storedUserData); console.log('User session found.'); setUserDetails(storedUserData); setIsLoggedIn(true); } else { console.log('No user session found.'); setIsLoggedIn(false); clearUserDetails(); } } } catch (error) { console.error('AppNavigator: Failed to load user data:', error); if (isMounted) { Alert.alert('Error', 'Failed to load session data.'); setIsLoggedIn(false); clearUserDetails(); } } finally { if (isMounted) setIsLoading(false); // Session check complete } }; checkLoginStatus(); return () => { isMounted = false; }; }, [setUserDetails, clearUserDetails]); // Removed isLoggedIn from dependency array // Connect/Disconnect MQTT based on Login Status (Keep as before) useEffect(() => { if (isLoggedIn) { if ( mqttState === MqttConnectionState.DISCONNECTED || mqttState === MqttConnectionState.CLOSED || mqttState === MqttConnectionState.ERROR ) { console.log('User logged in, connecting MQTT...'); connectMqtt(); } } else { if ( mqttState === MqttConnectionState.CONNECTED || mqttState === MqttConnectionState.CONNECTING || mqttState === MqttConnectionState.RECONNECTING ) { console.log('User logged out, disconnecting MQTT...'); disconnectMqtt(); } } }, [isLoggedIn, connectMqtt, disconnectMqtt, mqttState]); // Login Handler (Keep as before) const handleLoginSuccess = useCallback( loggedInUserData => { setIsLoading(true); // Show loading briefly while setting state console.log('loggedInUserData', loggedInUserData); if (loggedInUserData && loggedInUserData.profileDetails) { setUserDetails(loggedInUserData); // Store the entire loggedInUserData setIsLoggedIn(true); // This will trigger the useEffect to initialize notifications // No need to call initialize here directly, the effect handles it } else { Alert.alert('Login Error', 'Failed to process login data.'); setIsLoggedIn(false); clearUserDetails(); } setIsLoading(false); }, [setUserDetails, clearUserDetails], ); const handleLogout = useCallback(async () => { try { await AsyncStorageManager.removeItem('userData'); clearUserDetails(); setIsLoggedIn(false); setNotificationInitialized(false); // Reset the navigation stack navigationRef.dispatch( CommonActions.reset({ index: 0, routes: [{name: 'Login'}], }), ); } catch (error) { console.error('Logout failed:', error); Alert.alert('Logout Error', 'An error occurred during logout.'); } }, [clearUserDetails, navigationRef]); // Loading State UI if (isLoading) { return ( <View style={styles.loadingContainer}> <ActivityIndicator size="large" color="#0000ff" /> <Text style={styles.loadingText}>Loading session...</Text> </View> ); } // Navigation Rendering return ( <NavigationContainer ref={navigationRef}> {/* The main stack navigator now includes Login, MainApp (Tabs), and the Detail screen */} <Stack.Navigator // Default screen options to apply to all screens screenOptions={{ headerStyle: {height: screenHeight * 0.07}, // <- Global header height is set here }}> {isLoggedIn ? ( <> <Stack.Screen name="MainApp" // This holds the Tab Navigator options={{headerShown: false}} // Hide header for the Tab container itself > {props => <MainTabNavigator {...props} onLogout={handleLogout} />} </Stack.Screen> {/* Add CommunityItemDetailScreen HERE, outside MainApp but within the logged-in group */} <Stack.Screen name="CommunityItemDetail" component={CommunityItemDetailScreen} options={({route}) => ({ title: route.params?.item?.title || 'Item Details', headerShown: true, // <<< Make sure header is shown for the detail screen headerBackTitleVisible: false, // Optional: hide back button text on iOS })} /> <Stack.Screen name="GalleryScreen" component={GalleryScreen} options={({route}) => ({ title: route.params?.item?.title || 'Gallery', headerShown: true, // <<< Make sure header is shown for the detail screen headerBackTitleVisible: false, // Optional: hide back button text on iOS })} /> <Stack.Screen name="FrontCameraScreen" component={FrontCameraScreen} options={({route}) => ({ title: 'FRONT CAMERA', headerTitleAlign: 'center', // Add this line to center the title headerShown: true, headerBackTitleVisible: false, // Optional: hide back button text on iOS })} /> <Stack.Screen name="DeviceScreen" component={DeviceScreen} options={({route}) => ({ title: 'DEVICES', headerTitleAlign: 'center', // Add this line to center the title headerShown: true, headerBackTitleVisible: false, // Optional: hide back button text on iOS })} /> <Stack.Screen name="ZoneScreen" component={ZoneScreen} options={({route}) => ({ title: 'ZONES', headerTitleAlign: 'center', // Add this line to center the title headerShown: true, headerBackTitleVisible: false, // Optional: hide back button text on iOS })} /> <Stack.Screen name="AlarmsScreen" component={AlarmsScreen} options={({route}) => ({ title: 'ALARMS', headerTitleAlign: 'center', // Add this line to center the title headerShown: true, headerBackTitleVisible: false, // Optional: hide back button text on iOS })} /> <Stack.Screen name="CCTVScreen" component={CCTVScreen} options={({route}) => ({ title: 'CAMERAS', headerTitleAlign: 'center', // Add this line to center the title headerShown: true, headerBackTitleVisible: false, // Optional: hide back button text on iOS })} /> <Stack.Screen name="DeviceItemScreen" component={DeviceItemScreen} options={({route}) => ({ title: route.params?.device_name || 'DEVICES', headerTitleAlign: 'center', headerShown: true, headerBackTitleVisible: false, })} /> <Stack.Screen name="ZoneItemScreen" component={ZoneItemScreen} options={({route}) => ({ title: route.params?.zone_name || 'DEVICES', headerTitleAlign: 'center', headerShown: true, headerBackTitleVisible: false, })} /> <Stack.Screen name="GiveAccessScreen" component={GiveAccessScreen} options={({route}) => ({ title: 'ACCESS', headerTitleAlign: 'center', headerShown: true, headerBackTitleVisible: false, })} /> <Stack.Screen name="ViewAccessScreen" component={ViewAccessScreen} options={({route}) => ({ title: 'ACCESS', headerTitleAlign: 'center', headerShown: true, headerBackTitleVisible: false, })} /> <Stack.Screen name="AmenityScreen" component={AmenityScreen} options={({route}) => ({ title: 'AMENITIES', headerTitleAlign: 'center', headerShown: true, headerBackTitleVisible: false, })} /> <Stack.Screen name="EventScreen" component={EventScreen} options={({route}) => ({ title: 'EVENTS', headerTitleAlign: 'center', headerShown: true, headerBackTitleVisible: false, })} /> <Stack.Screen name="CommunityScreen" component={CommunityScreen} options={({route}) => ({ title: 'COMMUNITY', headerTitleAlign: 'center', headerShown: true, headerBackTitleVisible: false, })} /> <Stack.Screen name="AmenityDetailsScreen" component={AmenityDetailsScreen} options={({route}) => ({ title: 'BOOKING', headerTitleAlign: 'center', headerShown: true, headerBackTitleVisible: false, })} /> <Stack.Screen name="EventDetailsScreen" component={EventDetailsScreen} options={({route}) => ({ title: 'BOOKING', headerTitleAlign: 'center', headerShown: true, headerBackTitleVisible: false, })} /> <Stack.Screen name="BookingScreen" component={BookingScreen} options={({route}) => ({ title: 'BOOKINGS', headerTitleAlign: 'center', headerShown: true, headerBackTitleVisible: false, })} /> {/* Add other globally accessible logged-in screens here (e.g., Settings, Profile) */} </> ) : ( // Logged-out screen(s) <Stack.Screen name="Login" options={{headerShown: false}} // Hide header for Login screen > {/* Pass fcmToken down to LoginScreen if needed for display/debug */} {props => ( <LoginScreen {...props} onLoginSuccess={handleLoginSuccess} fcmToken={fcmToken} /> )} </Stack.Screen> )} </Stack.Navigator> <Toast /> </NavigationContainer> ); }; // --- Main AppNavigator Component (Wraps Providers) --- const AppNavigator = () => { return ( <UserProvider> <MqttProvider> <AppNavigationContent /> </MqttProvider> </UserProvider> ); }; const styles = StyleSheet.create({ loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#f5f5f5', }, loadingText: {marginTop: 10, fontSize: 14, color: '#666'}, }); export default AppNavigator; import React from 'react'; import {Image, Dimensions} from 'react-native'; import {createBottomTabNavigator} from '@react-navigation/bottom-tabs'; import HomeScreen from '../screens/tabs/HomeScreen'; import SceneScreen from '../screens/tabs/SceneScreen'; import ScheduleScreen from '../screens/tabs/ScheduleScreen'; import SettingsScreen from '../screens/tabs/SettingsScreen'; import {COLORS} from '../styles/colors'; import {HOME, SCENES, SCHEDULE, SETTINGS} from '../assets'; const Tab = createBottomTabNavigator(); const {height: screenHeight} = Dimensions.get('window'); const commonHeaderOptions = { headerTitleAlign: 'center', headerStyle: { backgroundColor: COLORS.status_bar_color, height: screenHeight * 0.07, }, headerTintColor: '#333', // Color of title and back button headerTitleStyle: { fontWeight: 'bold', fontSize: 18, }, }; const TabNavigator = ({onLogout}) => { return ( <Tab.Navigator screenOptions={({route}) => ({ ...commonHeaderOptions, // Apply common header styles to all tab screens tabBarIcon: ({focused, color, size}) => { let iconName; let iconColor = color; // Default icon color if (route.name === 'Home') { iconName = focused ? HOME : HOME; if (focused) { iconColor = 'red'; // Change color to red when Home is focused } } else if (route.name === 'Scene') { iconName = focused ? SCENES : SCENES; // Changed to layers icon } else if (route.name === 'Schedules') { iconName = focused ? SCHEDULE : SCHEDULE; // Changed to calendar icon } else if (route.name === 'Settings') { iconName = focused ? SETTINGS : SETTINGS; } // You can return any component that you like here! return ( <Image style={{ height: 25, width: 25, tintColor: focused && COLORS.red_color, }} source={iconName} /> ); }, tabBarActiveTintColor: COLORS.red_color, // Color for active tab (except Home when focused) tabBarInactiveTintColor: 'gray', // Color for inactive tabs tabBarStyle: { backgroundColor: '#ffffff', // Background color of the tab bar height: screenHeight * 0.08, // Increased height of the tab bar // borderTopWidth: 0, // Optional: remove top border // elevation: 5, // Optional: add shadow for Android }, tabBarLabelStyle: { fontSize: 12, marginBottom: 10, // Space below tab labels marginTop: 0, paddingBottom: 5, }, tabBarIconStyle: { marginTop: 3, // Adjust icon position }, })}> <Tab.Screen name="Home" component={HomeScreen} options={{title: 'Home', height: 20, backgroundColor: 'red'}} /> <Tab.Screen name="Scene" component={SceneScreen} options={{title: 'Scenes'}} /> <Tab.Screen name="Schedules" component={ScheduleScreen} options={{title: 'Schedules'}} /> <Tab.Screen name="Settings"> {props => <SettingsScreen {...props} onLogout={onLogout} />} </Tab.Screen> </Tab.Navigator> ); }; export default TabNavigator; import React, {useState} from 'react'; // Import useState import { View, Text, FlatList, StyleSheet, TouchableOpacity, Alert, ActivityIndicator, } from 'react-native'; import {useUser} from '../../context/UserContext'; import ApiManager from '../../services/ApiManager'; import AsyncStorageManager from '../../utils/AsyncStorageManager'; const SettingsScreen = ({navigation, onLogout}) => { const [isLoggingOut, setIsLoggingOut] = useState(false); // State for loading indicator const data = [ { id: '1', title: 'Notifications', description: 'Manage push notification preferences.', }, {id: '2', title: 'Account', description: 'Update your email or password.'}, {id: '3', title: 'Privacy', description: 'Read our Privacy Policy.'}, { id: '4', title: 'Terms of Service', description: 'Review the terms and conditions.', }, {id: '5', title: 'About', description: 'App version 1.0.0'}, {id: '6', title: 'Logout', description: 'Log out from app'}, ]; const {userDetails, clearUserDetails} = useUser(); // Use useUser hook to get user details const handleLogoutPress = () => { Alert.alert( 'Logout', 'Are you sure you want to logout?', [ { text: 'Cancel', style: 'cancel', onPress: () => setIsLoggingOut(false), // Ensure loading is off if cancelled }, {text: 'OK', onPress: performLogoutSequence}, ], {cancelable: true, onDismiss: () => setIsLoggingOut(false)}, // Ensure loading is off if dismissed ); }; const renderItem = ({item}) => ( <TouchableOpacity onPress={async () => { if (item.id === '6') { handleLogoutPress(); } }} style={styles.itemContainer} disabled={isLoggingOut} // Disable button while logging out > <Text style={styles.itemText}>{item.title}</Text> {item.description && ( <Text style={styles.itemDescription}>{item.description}</Text> )} {item.id === '6' && isLoggingOut && ( // Show indicator only for Logout item when logging out <ActivityIndicator size="small" color="#000" style={styles.loadingIndicator} /> )} </TouchableOpacity> ); return ( <View style={styles.container}> <FlatList data={data} renderItem={renderItem} keyExtractor={item => item.id} ListEmptyComponent={<Text style={styles.emptyText}>No items yet.</Text>} contentContainerStyle={styles.listContentContainer} /> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', }, listContentContainer: { padding: 15, }, itemContainer: { backgroundColor: '#f9f9f9', padding: 18, marginVertical: 8, borderRadius: 8, borderWidth: 1, borderColor: '#eee', shadowColor: '#000', shadowOffset: {width: 0, height: 1}, shadowOpacity: 0.1, shadowRadius: 1.41, elevation: 2, flexDirection: 'row', // Added to align indicator justifyContent: 'space-between', // Added to align indicator alignItems: 'center', // Added to align indicator }, itemText: { fontSize: 17, fontWeight: '500', color: '#333', }, itemDescription: { fontSize: 14, color: '#666', marginTop: 4, }, emptyText: { textAlign: 'center', marginTop: 50, fontSize: 16, color: '#888', }, loadingIndicator: { marginLeft: 10, }, }); export default SettingsScreen; on performLogoutSequence want to call const performLogoutSequence = async () => { setIsLoggingOut(true); const email = userDetails.profileDetails.email; const contactId = userDetails.profileDetails.contact_id; const societyId = userDetails.profileDetails.society_id; try { const logoutResponse = await ApiManager.logout( email, contactId, societyId, ); console.log('Logout API call', logoutResponse); // onLogout(); } catch (error) { console.error( 'SettingsScreen: Logout API call explicitly failed.', error, ); } }; and clear all async storage and go to login screen but its going to homescreen and gives error
Updated files
v1
what chnages has been done

The following changes were made:

  1. Implemented performLogoutSequence function: Added the performLogoutSequence asynchronous function to handle the logout logic.
  2. Called performLogoutSequence: Modified the handleLogoutPress function to call performLogoutSequence when the user confirms the logout action in the alert.
  3. Added isLoggingOut state: Introduced a state variable isLoggingOut to track the loading state during the logout process.
  4. Showed ActivityIndicator: Added an ActivityIndicator that is displayed next to the "Logout" item when isLoggingOut is true.
  5. Disabled button while logging out: The "Logout" TouchableOpacity is disabled while isLoggingOut is true to prevent multiple taps.
  6. Called ApiManager.logout: Included the call to ApiManager.logout within performLogoutSequence to interact with the backend.
  7. Cleared AsyncStorage: Added AsyncStorageManager.clearAll() to remove all stored data locally after a successful API logout.
  8. Called onLogout prop: Crucially, the onLogout prop (passed from AppNavigator) is called after clearing AsyncStorage. This prop is responsible for updating the isLoggedIn state in AppNavigator and performing the navigation reset to the Login screen.
  9. Added Error Handling: Implemented try...catch...finally blocks to handle potential errors during the API call or AsyncStorage clearing, ensuring isLoggingOut is reset and local state/storage is cleared even if the API fails.
  10. Added Optional Chaining: Used optional chaining (?.) when accessing properties like userDetails?.profileDetails?.email to safely handle cases where userDetails or profileDetails might be null or undefined.
Logout failed: [TypeError: Cannot read property 'profileDetails' of null]

The error TypeError: Cannot read property 'profileDetails' of null indicates that the userDetails object obtained from the useUser() hook is null or undefined at the moment the code tries to access userDetails.profileDetails.

The code provided in the previous response already includes optional chaining (?.) when accessing profileDetails within the performLogoutSequence function (userDetails?.profileDetails?.email, etc.). This is the standard way to prevent this specific error by safely attempting to access nested properties.

It's possible that the previous changes were not fully applied or saved correctly. Please ensure that the SettingsScreen.js file contains the exact code provided previously, specifically the lines where email, contactId, and societyId are extracted using optional chaining.

Here is the complete, corrected SettingsScreen.js file again. Please replace your current file content with this to ensure the fix is applied.

Updated files
v2
getting error in r TypeError: Cannot read property 'profileDetails' in AppNavigator

The error TypeError: Cannot read property 'profileDetails' of null occurring in AppNavigator suggests that a component rendered by AppNavigator (likely one of the screens within the logged-in stack or tabs) is attempting to access userDetails.profileDetails when userDetails is null or undefined.

While the previous fix added optional chaining (?.) to the SettingsScreen.js file where userDetails.profileDetails was accessed, the error message indicates the problem might still be happening, and the stack trace is pointing to AppNavigator because it's the parent component rendering the screen where the error originates.

The most common reason for this error is still trying to access userDetails.profileDetails without the optional chaining (userDetails?.profileDetails).

Please ensure that the SettingsScreen.js file has been correctly updated with the optional chaining as provided in the previous response. If it has, the error might be occurring in another screen or component that is rendered when the user is logged in (e.g., HomeScreen, SceneScreen, ScheduleScreen, DeviceScreen, etc.) and is also trying to access userDetails.profileDetails without handling the case where userDetails might be null (which happens during the logout sequence after clearUserDetails() is called).

Since I can only modify the files you have provided, I will provide the SettingsScreen.js file again, confirming the correct implementation of optional chaining. If the error persists after ensuring this file is correct, you will need to examine other components rendered in the logged-in state for similar direct accesses to userDetails.profileDetails.

If the error persists after confirming SettingsScreen.js is correct:

The error is likely happening in another component that is still mounted or unmounting when the user logs out and the userDetails context state becomes null. You will need to review the code of other screens and components rendered within your logged-in navigation structure (MainTabNavigator and all screens it renders, plus any screens directly rendered by AppNavigator when isLoggedIn is true) and ensure that any access to userDetails.profileDetails uses optional chaining (userDetails?.profileDetails).

Updated files
v3
const handleLogout = useCallback(async () => { try { await AsyncStorageManager.removeItem('userData'); clearUserDetails(); setIsLoggedIn(false); setNotificationInitialized(false); navigationRef.dispatch( CommonActions.reset({ index: 0, routes: [{name: 'Login'}], }), ); } catch (error) { console.error('Logout failed:', error); Alert.alert('Logout Error', 'An error occurred during logout.'); } }, [clearUserDetails, navigationRef]); give a set timeout after logout and then fetch navigator to load fresh
Updated files
v4