Home > database >  React Native && Firebase && useContext - "TypeError: null is not an object (evaluating '_r
React Native && Firebase && useContext - "TypeError: null is not an object (evaluating '_r

Time:05-18

The error is occurring in my useCachedResources.ts file but I'm not sure why. These are the three files that I'm working with. I suspect it has to do with the value being initially null for the first time but I'm conditionally rendering my Auth and App navigation stacks. Also, useCachedResources was provided with the template so maybe I should add the firebase onAuthStateChanged inside that?

AuthenticatedUserProvider.tsx

import { useState, createContext } from 'react';

export interface IUser {
  uuid: string;
  email: string | null;
}

export type AuthContextType = {
  user: IUser;
  setUser: (newUser: IUser | null) => void;
};

export const AuthenticatedUserContext = createContext<AuthContextType | null>(null);

export const AuthenticatedUserProvider = ({ children }: { children: React.ReactNode }) => {
  const [user, setUser] = useState<IUser | null>(null);

  return (
    <AuthenticatedUserContext.Provider value={user ? { user, setUser } : null}>
      {children}
    </AuthenticatedUserContext.Provider>
  );
};

navigation.ts

export default function Navigation({ colorScheme }: { colorScheme: ColorSchemeName }) {
  const { user } = useContext(AuthenticatedUserContext) as AuthContextType;

  return (
    <NavigationContainer linking={LinkingConfiguration} theme={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
      {user ? <AppStack /> : <AuthStack />}
    </NavigationContainer>
  );
}

useCachedResources.ts

export default function useCachedResources() {
  const { user, setUser } = useContext(AuthenticatedUserContext) as AuthContextType;
  const [isLoadingComplete, setLoadingComplete] = useState(false);

  useEffect(() => {
    async function loadResourcesAndDataAsync() {
      try {
        SplashScreen.preventAutoHideAsync();

        await Font.loadAsync({
          ...FontAwesome.font,
          'poppins-400': require('../assets/fonts/poppins-400.ttf'),
          'poppins-700': require('../assets/fonts/poppins-700.ttf'),
          'poppins-900': require('../assets/fonts/poppins-900.ttf'),
        });

        const unsubscribeAuthStateChanged = onAuthStateChanged(auth, (authenticatedUser) => {
          authenticatedUser ? setUser({ uuid: authenticatedUser.uid, email: authenticatedUser.email }) : setUser(null);
        });

        return unsubscribeAuthStateChanged;
      } catch (e) {
        console.warn(e);
      } finally {
        setLoadingComplete(true);
        SplashScreen.hideAsync();
      }
    }

    loadResourcesAndDataAsync();
  }, [user]);

  return isLoadingComplete;
}

enter image description here

CodePudding user response:

Because you initialise your context with null, you cannot use destructuring. For example, this...

const { user } = null;

in the browser generates the following error

Uncaught TypeError: Cannot destructure property 'user' of 'null' as it is null.

My advice would be to initialise your context with a dummy value. This will also save you having to use as AuthContextType everywhere

export type AuthContextType = {
  user: IUser | null; // make user optional instead of the context
  setUser: (newUser: IUser | null) => void;
};

// initialise with a dummy / no-op context
export const AuthenticatedUserContext = createContext<AuthContextType>({
  user: null,
  setUser: () => {}
});

export const AuthenticatedUserProvider: React.FC = ({ children }) => {
  const [user, setUser] = useState<IUser | null>(null);

  return (
    <AuthenticatedUserContext.Provider value={{ user, setUser }}>
      {children}
    </AuthenticatedUserContext.Provider>
  );
};
  • Related