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;
}
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>
);
};