I have implemented realm device sync and I am using email/password authentication method to login. I am following the steps provided in the template and I have placed the Navigation Container at Top level that Realm’s AppProvider and User Provider Component. When the user is not logged in I am being show the Login page. However I am unable to move to Signup Page that is being navigated from Login Page.
Code for App.tsx
import "react-native-gesture-handler";
import { NavigationContainer } from "@react-navigation/native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import NativeStack from "./src/routes/NativeStack";
import { SafeAreaProvider } from "react-native-safe-area-context";
import { PaperProvider } from "react-native-paper";
import { AppProvider, UserProvider } from "@realm/react";
import { appId, baseUrl } from "./atlasConfig.json";
import Login from "./src/screens/authScreen/Login";
import realmContext from "./src/data/dbContext";
import Loading from "./src/screens/loadingScreen/Loading";
export default function App() {
// Getting Realm Provider
const { RealmProvider } = realmContext;
return (
<PaperProvider>
<SafeAreaProvider>
<GestureHandlerRootView style={{ flex: 1 }}>
<NavigationContainer>
<AppProvider id={appId} baseUrl={baseUrl}>
<UserProvider fallback={Login}>
<RealmProvider
sync={{
flexible: true,
onError: (_, error) => {
console.log(error);
},
initialSubscriptions: {
update(subs, realm) {
subs.add(realm.objects("Company")),
subs.add(realm.objects("Purchase")),
subs.add(realm.objects("Payment"));
},
},
}}
fallback={Loading}
>
<NativeStack />
</RealmProvider>
</UserProvider>
</AppProvider>
</NavigationContainer>
</GestureHandlerRootView>
</SafeAreaProvider>
</PaperProvider>
);
}
code for Login.tsx
import Realm from "realm";
import {
Keyboard,
NativeSyntheticEvent,
StyleSheet,
TextInputChangeEventData,
TouchableWithoutFeedback,
View,
} from "react-native";
import React, { useCallback, useState } from "react";
import { StackActions, useNavigation } from "@react-navigation/native";
import { SafeAreaView } from "react-native-safe-area-context";
import { Button, Text, TextInput } from "react-native-paper";
import { useApp } from "@realm/react";
const Login = () => {
// Realm App Context
const app = useApp();
// Navigation
const nav = useNavigation();
// Form input variables
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
// Toggle Password Visibility
const [passwordVisibility, setPasswordVisibility] = useState(true);
// Action Handlers
const formClear = () => {
setEmail("");
setPassword("");
setPasswordVisibility(true);
};
const registerHandler = () => {
nav.dispatch(StackActions.replace("Register"));
formClear();
};
const loginHandler = useCallback(async () => {
const creds = Realm.Credentials.emailPassword({ email, password });
await app.logIn(creds);
formClear();
}, [app, email, password]);
return (
<TouchableWithoutFeedback
onPress={Keyboard.dismiss}
style={styles.containerWrapper}
>
<SafeAreaView style={styles.containerWrapper}>
<View style={styles.container}>
<View style={styles.headerContainer}>
<Text variant="titleLarge" style={styles.headerLabel}>
Register
</Text>
</View>
<View>
<TextInput
label="Email"
placeholder="Email"
value={email}
autoCapitalize="none"
onChange={(e: NativeSyntheticEvent<TextInputChangeEventData>) => {
setEmail(e.nativeEvent.text);
}}
/>
<TextInput
label="Password"
placeholder="Password"
value={password}
autoCapitalize="none"
secureTextEntry={passwordVisibility}
onChange={(e: NativeSyntheticEvent<TextInputChangeEventData>) => {
setPassword(e.nativeEvent.text);
}}
right={
<TextInput.Icon
icon="eye"
onPress={() => {
setPasswordVisibility((previousState) => !previousState);
}}
/>
}
/>
</View>
<View style={styles.buttonContainer}>
<Button mode="contained" onPress={loginHandler}>
Login
</Button>
<Button mode="outlined" onPress={registerHandler}>
Don't have an Account? Register
</Button>
</View>
</View>
</SafeAreaView>
</TouchableWithoutFeedback>
);
};
export default Login;
const styles = StyleSheet.create({
containerWrapper: {
flex: 1,
},
container: {
flex: 1,
},
headerContainer: {},
headerLabel: {},
inputContainer: {
gap: 20,
},
buttonContainer: {},
button: {},
});
code for Signup.tsx
import Realm from "realm";
import {
Alert,
Keyboard,
NativeSyntheticEvent,
StyleSheet,
TextInputChangeEventData,
TouchableWithoutFeedback,
View,
} from "react-native";
import React, { useCallback, useState } from "react";
import { SafeAreaView } from "react-native-safe-area-context";
import { Button, Text, TextInput } from "react-native-paper";
import { StackActions, useNavigation } from "@react-navigation/native";
import { useApp } from "@realm/react";
const Register = () => {
// App Context for Realm
const app = useApp();
// Navigation
const nav = useNavigation();
// Form input variables
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
// Toggle Password Visibility
const [passwordVisibility, setPasswordVisibility] = useState(true);
const [confirmPasswordVisibility, setConfirmPasswordVisibility] =
useState(true);
// Action Handlers
const formClear = () => {
setEmail("");
setPassword("");
setConfirmPassword("");
setPasswordVisibility(true);
setConfirmPasswordVisibility(true);
};
const signIn = useCallback(async () => {
const creds = Realm.Credentials.emailPassword({ email, password });
await app.logIn(creds);
}, [app, email, password]);
const registerHandler = useCallback(async () => {
try {
await app.emailPasswordAuth.registerUser({ email, password });
await signIn();
formClear();
} catch (error: any) {
Alert.alert(`Failed to Register: ${error?.message}`);
}
}, [signIn, app, email, password]);
const loginHandler = () => {
nav.dispatch(StackActions.replace("Login"));
formClear();
};
return (
<TouchableWithoutFeedback
onPress={Keyboard.dismiss}
style={styles.containerWrapper}
>
<SafeAreaView style={styles.containerWrapper}>
<View style={styles.container}>
<View style={styles.headerContainer}>
<Text variant="displayLarge" style={styles.headerLabel}>
Register
</Text>
</View>
<View style={styles.formContainer}>
<TextInput
label="Email"
placeholder="Email"
value={email}
autoCapitalize="none"
onChange={(e: NativeSyntheticEvent<TextInputChangeEventData>) => {
setEmail(e.nativeEvent.text);
}}
/>
<TextInput
label="Password"
placeholder="Password"
value={password}
autoCapitalize="none"
secureTextEntry={passwordVisibility}
onChange={(e: NativeSyntheticEvent<TextInputChangeEventData>) => {
setPassword(e.nativeEvent.text);
}}
right={
<TextInput.Icon
icon="eye"
onPress={() => {
setPasswordVisibility((previousState) => !previousState);
}}
/>
}
/>
<TextInput
label="Confirm Password"
placeholder="Re-Enter Password"
value={confirmPassword}
autoCapitalize="none"
secureTextEntry={confirmPasswordVisibility}
onChange={(e: NativeSyntheticEvent<TextInputChangeEventData>) => {
setConfirmPassword(e.nativeEvent.text);
}}
right={
<TextInput.Icon
icon="eye"
onPress={() => {
setConfirmPasswordVisibility(
(previousState) => !previousState
);
}}
/>
}
/>
</View>
<View style={styles.buttonContainer}>
<Button mode="contained" onPress={registerHandler}>
Register
</Button>
<Button mode="outlined" onPress={loginHandler}>
Already have an Account? Login
</Button>
</View>
</View>
</SafeAreaView>
</TouchableWithoutFeedback>
);
};
export default Register;
const styles = StyleSheet.create({
containerWrapper: {
flex: 1,
},
container: {
flex: 1,
},
headerContainer: {
height: 80,
},
headerLabel: {},
inputContainer: {
gap: 20,
},
buttonContainer: {},
button: {},
formContainer: {
flex: 1,
gap: 20,
},
});
I tried moving the navigation container above the Realm Provider and App Provider to see if the navigation object is provided post that. No resolution as of now yet.
2
Answers
I am posting this answer at a later point of time. The UserProvider of Realm under fallback requires a separate navigation stack setup and it was because of this I had the issue of uninitialised navigation object error.
I just had to add another separate navigation stack with a separate navigation container to the fallback option to resolve this issue.
Can you provide the code demonstrating exactly how you did this?