ERROR TypeError: Cannot read property 'user' of undefined | React Native

123 Views Asked by At

I am currently getting the undefined error. I want to render screens based on the user authentication state. Here's my App.js:

import { StyleSheet } from "react-native";
import { SafeAreaProvider } from "react-native-safe-area-context";
import * as SplashScreen from "expo-splash-screen";
import {
  WelcomeScreen,
  LoginScreen,
  RegisterScreen,
  MapScreen,
  BookingScreen,
  ConfirmedScreen,
  PaymentScreen,
  PaymentConfirmScreen,
  CardPaymentScreen,
  ReviewScreen,
} from "./screens";
import * as React from "react";
import Toast from "react-native-toast-message";
import { useFonts } from "expo-font";
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import BottomTabNavigation from "./navigation/BottomTabNavigation";
import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { StripeProvider } from "@stripe/stripe-react-native";
import { PUBLISHABLE_KEY } from "@env";
import AuthProvider from "./contexts/AuthProvider";
import useAuth from "./hook/useAuth";
import { useCallback } from "react";

const Stack = createNativeStackNavigator();

const queryClient = new QueryClient();

const App = () => {
  const { user } = useAuth(); // THIS IS CREATING THE ERROR

  const [fontsLoaded] = useFonts({
    DMBold: require("./assets/fonts/DMSans-Bold.ttf"),
    DMMedium: require("./assets/fonts/DMSans-Medium.ttf"),
    DMRegular: require("./assets/fonts/DMSans-Regular.ttf"),
    InterBold: require("./assets/fonts/Inter-Bold.ttf"),
    InterSemiBold: require("./assets/fonts/Inter-SemiBold.ttf"),
    InterMedium: require("./assets/fonts/Inter-Medium.ttf"),
    InterRegular: require("./assets/fonts/Inter-Regular.ttf"),
  });

  const onLayoutRootView = useCallback(async () => {
    if (!fontsLoaded) {
      await SplashScreen.hideAsync();
    }
  }, [fontsLoaded]);

  if (!fontsLoaded) {
    return null;
  }

  return (
    <AuthProvider>
      <QueryClientProvider client={queryClient}>
        <GestureHandlerRootView style={{ flex: 1 }}>
          <BottomSheetModalProvider>
            <NavigationContainer>
              <StripeProvider publishableKey={PUBLISHABLE_KEY}>
                <SafeAreaProvider>
                  <Stack.Navigator initialRouteName="welcome" mode="modal">
                    <Stack.Screen
                      name="welcome"
                      component={WelcomeScreen}
                      options={{ headerShown: false }}
                    />

                    <Stack.Screen
                      name="login"
                      component={LoginScreen}
                      options={{ headerShown: false }}
                    />

                    <Stack.Screen
                      name="register"
                      component={RegisterScreen}
                      options={{ headerShown: false }}
                    />

                    <Stack.Screen
                      name="main"
                      component={BottomTabNavigation}
                      options={{ headerShown: false }}
                    />

                    <Stack.Screen
                      name="map"
                      component={MapScreen}
                      options={{ headerShown: false }}
                    />

                    <Stack.Screen name="booking" component={BookingScreen} />

                    <Stack.Screen
                      name="confirmed"
                      component={ConfirmedScreen}
                      options={{ headerShown: false }}
                    />

                    <Stack.Screen name="payment" component={PaymentScreen} />

                    <Stack.Screen
                      name="card-payment"
                      component={CardPaymentScreen}
                      options={{ headerShown: false }}
                    />

                    <Stack.Screen
                      name="payment-confirm"
                      component={PaymentConfirmScreen}
                      options={{ headerShown: false }}
                    />
                    <Stack.Screen name="review" component={ReviewScreen} />
                  </Stack.Navigator>

                  <Toast position="bottom" bottomOffset={20} />
                </SafeAreaProvider>
              </StripeProvider>
            </NavigationContainer>
          </BottomSheetModalProvider>
        </GestureHandlerRootView>
      </QueryClientProvider>
    </AuthProvider>
  );
};

export default App;

const styles = StyleSheet.create({});

And here's my AuthProvider.jsx

import React, { createContext, useEffect, useState } from "react";
import {
  GoogleAuthProvider,
  createUserWithEmailAndPassword,
  getAuth,
  onAuthStateChanged,
  signInWithEmailAndPassword,
  signInWithPopup,
  signOut,
  updateProfile,
  sendEmailVerification,
} from "firebase/auth";
import { app } from "../firebase.config";
import { View, Text, TouchableOpacity } from "react-native";
import AsyncStorage from "@react-native-async-storage/async-storage";
import axios from "axios";
import { API_URL } from "@env";
import { initializeAuth, getReactNativePersistence } from "firebase/auth";
import ReactNativeAsyncStorage from "@react-native-async-storage/async-storage";

export const AuthContext = createContext();

const auth = initializeAuth(app, {
  persistence: getReactNativePersistence(ReactNativeAsyncStorage),
});

const googleProvider = new GoogleAuthProvider();

const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  const [authLoading, setAuthLoading] = useState(true);

  const createUser = (email, password) => {
    setAuthLoading(true);
    return createUserWithEmailAndPassword(auth, email, password);
  };

  const updateUserInfo = (name, photoLink) => {
    setAuthLoading(true);
    return updateProfile(auth.currentUser, {
      displayName: name,
      photoURL: photoLink,
    });
  };

  const logOut = () => {
    setAuthLoading(true);
    return signOut(auth);
  };

  const signIn = (email, password) => {
    setAuthLoading(true);
    return signInWithEmailAndPassword(auth, email, password);
  };

  const signInWithGoogle = () => {
    return signInWithPopup(auth, googleProvider);
  };

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
      setUser(currentUser);
      console.log("current user", currentUser);
      // setAuthLoading(false);

      // get and set token
      if (currentUser) {
        axios
          .post(`${API_URL}/jwt`, {
            email: currentUser.email,
          })
          .then((data) => {
            AsyncStorage.setItem("access-token", data.data.token);
            setAuthLoading(false);
          });
      } else {
        AsyncStorage.removeItem("access-token");
      }
    });

    return () => {
      return unsubscribe();
    };
  }, []);

  const authInfo = {
    user,
    authLoading,
    createUser,
    updateUserInfo,
    logOut,
    signIn,
    signInWithGoogle,
    sendEmailVerification,
  };

  return (
    <AuthContext.Provider value={authInfo}>{children}</AuthContext.Provider>
  );
};

export default AuthProvider;

And the useAuth hook:

import { useContext } from "react";
import { AuthContext } from "../contexts/AuthProvider";

const useAuth = () => {
  const auth = useContext(AuthContext);
  return auth;
};

export default useAuth;

I am getting the error when I call the useAuth hook in my App.js.

Please help. Thank you.

My RegisterScreen doesn't show any error while call the useAuth hook but I get it in App.js.

0

There are 0 best solutions below