Search code examples
reactjsreact-nativeexporeact-navigationreact-context

React Native - Expo problem when state change


this is the first app where I work with react native and expo. this is

"expo": "~49.0.13",
"react-native": "0.72.6",
"@react-navigation/drawer": "^6.6.5",
"@react-navigation/native": "^6.1.9",
"@react-navigation/native-stack": "^6.9.15",

I'm having a very strange problem with my application.

I have a global state of the application created with useContext.

import { UserState } from '@models/userModel';
import React, { createContext, useReducer } from 'react';

const initialState: UserState = {
    isAuthenticated: false,
    userData: null
};
export type TypeContext = {
    state: UserState,
    dispatch: React.Dispatch<any>;
};
const UserContext = createContext<TypeContext>({
    state: initialState,
    dispatch: () => { }
});

const { Provider } = UserContext;
type Props = {
    children: any;
};
type Actions =
    | {
        type: 'set_user',
        userData: any;
    }
    |{
        type: 'is_authenticated',
        isAuthenticated: boolean
    }
    | {
        type: 'delete_user'
    }
    | {
        type: 'update_user',
        userData: any
    };

export enum TypesInContext {
    set_user = 'set_user',
    is_authenticated = 'is_authenticated',
    delete_user = 'delete_user',
    update_user = 'update_user'
};

const reducer = (state: UserState, action: Actions): UserState =>{
    switch (action.type) {
        case TypesInContext.set_user:
            return{
                ...state,
                userData: action.userData,
            };
        case TypesInContext.is_authenticated:
            console.log("ACCCCTIOOON--->",action)
            return {
                ...state,
                isAuthenticated: action.isAuthenticated
            }
        case TypesInContext.delete_user:
            return{
                ...state,
                userData: null,
                isAuthenticated: false
            }
        case TypesInContext.update_user:
            return {
                ...state,
                userData: action.userData
            }
        default:
            return state;
    }
};

const UserProvider = ({children}:Props) =>{
    const [state,dispatch]= useReducer(reducer,initialState);
    return <Provider value={{state,dispatch}}>{children}</Provider>
};
export {UserContext,UserProvider}

I use this status in my index to be able to switch screens. Obviously I wrap my app with the context as indicated in the documentation.

import { StatusBar } from 'expo-status-bar';
import Index from './src/Index';
import 'react-native-gesture-handler';
import { UserProvider } from '@src/store/userContext';

export default function App() {
  return (
    <UserProvider>
      <StatusBar style='auto'/>
      <Index />
    </UserProvider>
  );
}

and use here

import React, { useContext } from 'react';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { NavigationContainer } from '@react-navigation/native';
import LoginScreen from '@screens/LoginScreen';
import { RootTypesNavigation } from '@navigation/typesNavigation';
import DrawerNavigation from '@navigation/DrawerNavigation';
import { UserContext } from './store/userContext';
export default function Index() {
    const Stack = createNativeStackNavigator<RootTypesNavigation>();
    const { state } = useContext(UserContext);
    console.log("🚀 ~ file: Index.tsx:11 ~ Index ~ state:", state)
    
    return (
        <NavigationContainer>
            <Stack.Navigator initialRouteName='Login'>
                {state.isAuthenticated ?
                    <Stack.Screen name='DrawerNavigation' component={DrawerNavigation} options={{ headerShown: false }} />
                    :
                    <Stack.Screen name='Login' component={LoginScreen} options={
                        { headerShown: false }
                    } />
                }
            </Stack.Navigator>
        </NavigationContainer>
    );
}

and the state in this particular case, we alter it from LoginScreen in these functions (I did a little demo here)

   const submitLogin = async () => {
        if (!isEmailValid(email)) {
            notifyMessage('Error', "El email ingresado no es válido.")
            return;
        }
        let objetAuthetication: ParametersLogin = {
            username: email,
            password: password,
            domain: domain,
            deviceInfo: {
                available: true,
                platform: Device.osName,
                version: Device.osVersion,
                uuid: null,
                cordova: "react-native 0.72.6",
                model: domain,
                manufacturer: Device.brand,
                isVirtual: false,
                serial: null,
                appVersion: "1.0.11"
            }
        }
        const response = await authLogin(objetAuthetication);
        if (response && response.status == 'OK') {
            setAlertLoginOk(true);
            dispatch({
                type: TypesInContext.set_user,
                userData: response.result,
            })
        } else {
            alertAccessDenied();
        }
    }

by business logic, first I have to do a dispatch to save the user data, and then in another confirmation I do the dispatch of the isAuthenticated, where here I alter the state where it shows one screen or another.

   const emailConfirmationAndAuthentication = async () => {
        if (newEmail) {
            console.log("Se registro nuevo email");
            //Que hacer aca? Update? en la web no hace nada.
        };
        setAlertLoginOk(false);
        console.log("🚀 ~ file: LoginScreen.tsx:78 ~ emailConfirmationAndAuthentication ~ navigation:", navigation)
        // navigation.navigate('DrawerNavigation',{screen:'Home'})
        setTimeout(()=>{
            dispatch({
                type: TypesInContext.is_authenticated,
                isAuthenticated: true
            });
        },1000)
    };

that horrible setTimeOut was the solution for my app not to break (thanks to a thread in this community), but I really want to understand what is going on or what is the error that occurs or what am I doing wrong.

Did anyone come across this? Am I doing something wrong? What is the problem? Did I miss some of the documentation?

I've been having these problems for several days now and I don't understand! Thank you very much!


Solution

  • luckily I was able to find the problem. It was the react-navigation/native-stack library. I changed that library, for react-navigation/stack and all my problems with the state have been solved. I hope this is helpful for someone.