React Native Navigator always displays the same page - javascript

I am using Stack based navigation.
My Navigator looks like this:
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="AddItemsScreen" component={AddItemsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
The Home screen is rendered by default. At the bottom of the following component, there is a button to navigate to the AddItemsScreen:
function EntriesList({db, setDb, path, entries}) {
const AddButtonPress = (event) => {
navigation.navigate('AddItemsScreen', { db: db, setDb: setDb, path: path, entries: entries })
}
const entryListItems = [];
for (const [idx, entry] of entries.entries()) {
if (entry.type == "container") {
entryListItems.push(<li key={idx}><EntryListItem_ContainerType entry={entry}></EntryListItem_ContainerType></li>)
} else if (entry.type == "item") {
entryListItems.push(<li key={idx}><EntryListItem_ItemType entry={entry}></EntryListItem_ItemType></li>)
}
}
return (
<div>
<ul>{entryListItems}</ul>
<Button onPress={AddButtonPress} title="Add Items"></Button>
</div>
)
}
The AddItemScreen itself is very simple right now:
function AddItemsScreen(props) {
const BackButtonPress = () => {
navigation.navigate('HomeScreen', { name: 'Jane' })
}
return (
<div>
<Button onPress={BackButtonPress} title={props.route.params.path}></Button>
<Text>Items screen</Text>
</div>
)
}
The problem is, when I click the "Add Items" button, the Home page is still getting rendered. The URL changes to "/AddItemsScreen" but the content never actually shows. Not sure what I need to fix, and appreciate any help.

Figured this out using question React-navigation change url but not view
I needed to add linking attribute like so:
const linking = {
prefixes: ['http://localhost:19006/']
};
function App() {
return (
<NavigationContainer linking={linking}>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="AddItemsScreen" component={AddItemsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
Not sure if this is just needed because I'm developing on web.

Related

Blink screen when i navigate React-native

Why is it that when I navigate from the first screen to the second screen, there is a white blink on the left side of the screen?
https://imgur.com/mEKw9DH
first screen code:
const Stack = createStackNavigator();
function MyStack() {
return (
<Stack.Navigator
screenOptions={{
headerShown: false,
}}>
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Cadastrar" component={Cadastrar} />
</Stack.Navigator>
);
}
export default function App() {
return (
<NavigationContainer>
<MyStack />
</NavigationContainer>
);
}
Second screen code:
const register = () => {
navigation.navigate("Cadastrar")
}

onAuthStateChanged not navigating me to home screen

I have been struggling with this for days now... When I try to login it does not automatically navigate me to home screen, unless I close and open app.
I am working with 3 separate components. However after I register once, this issue doesn't occur it only happens with new accounts.
const [userAuth, setUserAuth] = useState()
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(user => {
user && auth.currentUser?.emailVerified ? setUserAuth(true) : setUserAuth(false)
})
return unsubscribe
})
return (
<Provider store={store}>
<NavigationContainer>
<Drawer.Navigator>
<Drawer.Screen
name='main'
component={userAuth ? MainDrawer : AuthStack}
/>
</Drawer.Navigator>
</NavigationContainer>
</Provider>
This is my MainDrawer.js
export default function MainDrawer() {
return (
<Drawer.Navigator drawerContent={props => <DrawerScreen {...props} />}>
<Drawer.Screen
name='drawer'
component={HomeStack}
options={{ headerShown: false }}
/>
</Drawer.Navigator>
)
}
This is my HomeStack.js
<Stack.Navigator initialRouteName='home'>
<Stack.Screen name='home' component={TabNav}/>
<Stack.Screen name='about' component={AboutUs}/>
<Stack.Screen name='faq' component={FaqScreen}/>
<Stack.Screen name='userTerms' component={TermsScreen} />
<Stack.Screen name='privacy' component={PrivacyScreen} />
</Stack.Navigator>
I believe the issue is where you're trying to call onAuthStateChanged, it should be auth().onAuthStateChanged(...). Also, I would change it a little bit
useEffect(() => {
const unsubscribe = auth().onAuthStateChanged(firebaseUserResult => {
firebaseUserResult && firebaseUserResult.emailVerified ? setUserAuth(true) : setUserAuth(false)
})
return unsubscribe
})

React Native navigation problem with asyc storage

I have a really strange problem and so far could not identify the possible weak points.
I have a firebase & react-native application with a nested login.
The problem is the navigation. The drawer and the stack navigation works fine, except for the login screen.
When I try to login and the isSignedIn variable becomes true, the screen is not changing for the home screen. It remains the login screen. However, on the console, I see the authentication was successful. If I go back to the code and click a save in the Navigation file (which was posted here), the forced reload seems to solve the problem and I can see the home screen. But this is not automatically.
So confused.
export default function Navigator() {
const user = firebase.auth().currentUser;
const [isSignedIn, setIsSignedIn] = useState(false)
useEffect( () => {
user ? setIsSignedIn(true) : setIsSignedIn(false)
if (isSignedIn) storeData(user)
})
const storeData = async (value) => {
try {
await AsyncStorage.setItem('userObject', JSON.stringify(value))
alert('Data successfully saved to the storage')
} catch (e) {
alert('Failed to save the data to the storage')
}
}
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Login">
{isSignedIn ? (
<>
<Stack.Screen name='loggedInUserNavigationStack' component={loggedInUserNavigationStack} options={{headerShown: false}}>
</Stack.Screen>
</>
) : (
<Stack.Screen name="Log in" component={LoginScreen} options={{headerShown: false}} />
)}
</Stack.Navigator>
</NavigationContainer>
);
}
function loggedInUserNavigationStack() {
return (
<Drawer.Navigator initialRouteName="mainContentNavigator" drawerContent={ props => <DrawerContent {...props} /> }>
<Drawer.Screen name="mainContentNavigator" component={mainContentNavigator} options={{ title: 'Home' }} />
<Drawer.Screen name="About" component={AboutScreen} options={{ title: 'About' }}/>
<Drawer.Screen name="Archive" component={ArchiveScreen} options={{ title: 'Archive' }}/>
<Drawer.Screen name="Profile" component={ProfileScreen} options={{ title: 'Profile' }} />
<Drawer.Screen name="Sign Out" component={SignOutScreen} options={{headerShown: false}}/>
</Drawer.Navigator>
);
}
function mainContentNavigator() {
return (
<Stack.Navigator initialRouteName="HomeScreen">
<Stack.Screen name="Home" component={HomeScreen} options={
({ navigation }) => {
return {
headerLeft: () => <Header navigation={navigation} />
}
}
} />
<Stack.Screen name="CertainHabbit" component={CertainHabbit} options={({ route }) => ({ title: route.params.name })} />
</Stack.Navigator>
);
}
Firebase automatically tries to restore the signed-in user when you load the page. But since this requires an async call to the server, it may take some time. Right now, by the time your const user = firebase.auth().currentUser runs, the user hasn't been authenticated yet. And by the time the authentication finishes, your code isn't aware of it.
The solution is to use an auth state listener, as shown in the first snippet of the documentation on determining the signed in user. For you that'd be something like this:
useEffect( () => {
firebase.auth().onAuthStateChanged((user) => {
setIsSignedIn(!!user);
if (user) storeData(user)
});
})
Please try,
<NavigationContainer>
<Stack.Screen name="Login" component={LoginScreen} options={{headerShown: false}} />
</NavigationContainer>
You have given initial route name has initialRouteName="Login"
I do see a space in the stack screen.
Try the following for return part:
return isSignedIn ? (
<NavigationContainer>
<Stack.Screen name='loggedInUserNavigationStack' component={loggedInUserNavigationStack} options={{headerShown: false}} />
</NavigationContainer>
):(
<NavigationContainer>
<Stack.Screen name="Login" component={LoginScreen} options={{headerShown: false}} />
</NavigationContainer>
)

On first launch choose language React Native

I'm new to react native and using I18n to translate my multi language app according to user choice.
My main goal here is to show screen on first entry to the app that will decide the user language by his choice.
import React from 'react';
import Login from './login.js';
import Register from './register.js';
import Dashboard from './dashboard.js';
import { NavigationContainer, useNavigation } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { Text, View, Button } from 'react-native';
import AsyncStorage from '#react-native-async-storage/async-storage';
const HAS_LAUNCHED = 'hasLaunched';
const ENGLISH = 'en';
const HEBREW = 'he';
function setAppLaunched(en) {
const navigation = useNavigation();
AsyncStorage.setItem(HAS_LAUNCHED, 'true');
AsyncStorage.setItem(en ? ENGLISH : HEBREW, 'true');
navigation.navigate('Login');
}
async function checkIfFirstLaunch() {
try {
const hasLaunched = await AsyncStorage.getItem(HAS_LAUNCHED);
if (hasLaunched === null) {
return (
<View>
<Text>Choose Language</Text>
<Button onPress={() => setAppLaunched(false)} title="Hebrew"/>
<Button onPress={() => setAppLaunched(true)} title="English"/>
</View>
);
}
return false;
} catch (error) {
return false;
}
}
console.log(checkIfFirstLaunch());
const Stack = createStackNavigator();
export default function App() {
return (
<>
<NavigationContainer>
<Stack.Navigator screenOptions={{headerShown: false}} initialRouteName="Login">
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Register" component={Register} />
<Stack.Screen name="Dashboard" component={Dashboard} />
</Stack.Navigator>
</NavigationContainer>
</>
);
}
Once checkIfFirstLaunch() is triggered AsyncStorage saves the user language and remember it to the next times he launches the app.
I have created the syntax that will detect if user launched the app for the first time, but how can I actually show that screen?
How to display first launch screen using functional components?
EDIT
export default function App() {
return (
<>
<CheckIfFirstLaunch/>
<NavigationContainer>
<Stack.Navigator screenOptions={{headerShown: false}} initialRouteName="Login">
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Register" component={Register} />
<Stack.Screen name="Dashboard" component={Dashboard} />
</Stack.Navigator>
</NavigationContainer>
</>
);
}
If I try that it returns the following
Error: Objects are not valid as a React child (found: object with keys
{_U, _V, _W, _X}). If you meant to render a collection of children,
use an array instead.
The error is because you're rendering a Functional Component that you defined as an async function.
Now I don't know if you want to show the "Choice" and the navigation screens at the same time. If you want that, maintain the structure you have and do this.
function checkIfFirstLaunch() {
const [selected, setSelected] = useState(false);
if (selected) return null;
const verifyHasLaunched = async () => {
try {
const hasLaunched = await AsyncStorage.getItem(HAS_LAUNCHED);
setSelected(hasLaunched != null)
} catch (err) {
setSelected(false);
}
}
useEffect(verifyHasLaunched);
const selectLaunched = (value) => {
setAppLaunched(value);
setSelected(true);
};
return (
<View>
<Text>Choose Language</Text>
<Button onPress={() => selectLaunched(false)} title="Hebrew"/>
<Button onPress={() => selectLaunched(true)} title="English"/>
</View>
);
}
If you need to change the render as in render either the "Choose screen" or the Navigation component
Then you have to change both App and checkIfFirstLaunch;
function checkIfFirstLaunch({ onSelect }) {
const selectLaunched = (value) => {
setAppLaunched(value);
onSelect();
};
return (
<View>
<Text>Choose Language</Text>
<Button onPress={() => selectLaunched(false)} title="Hebrew"/>
<Button onPress={() => selectLaunched(true)} title="English"/>
</View>
);
}
export default class App extends React.Component {
state = {
selected: false;
};
async componentDidMount() {
try {
const hasLaunched = await AsyncStorage.getItem(HAS_LAUNCHED);
this.setState({ selected: hasLaunched != null })
} catch (err) {
// Something went wrong
}
}
render() {
if (!this.state.selected) return <CheckIfFirstLaunch onSelect={() => this.setState({ selected: true })} />
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{headerShown: false}} initialRouteName="Login">
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Register" component={Register} />
<Stack.Screen name="Dashboard" component={Dashboard} />
</Stack.Navigator>
</NavigationContainer>
);
}
}

Using Firebase Custom Claims to conditionally render a UI in React Native

I'm currently building a react native project with firebase and I would like to use the custom claims to conditionally render a Navigator.
Looking at the firebase documentation the example they give is:
firebase.auth().currentUser.getIdTokenResult()
.then((idTokenResult) => {
// Confirm the user is an Admin.
if (!!idTokenResult.claims.admin) {
// Show admin UI.
showAdminUI();
} else {
// Show regular user UI.
showRegularUI();
}
})
.catch((error) => {
console.log(error);
});
So trying to convert it to react native this is what I came up with:
export default function UINav () {
return firebase
.auth()
.currentUser.getIdTokenResult()
.then((tokenResult) => {
if (tokenResult.claims.admin) {
return (
<NavigationContainer>
<Stack.Navigator
mode="modal"
>
<Stack.Screen
name="Admin"
component={AdminNavigator}
/>
</Stack.Navigator>
</NavigationContainer>
)
} else {
return (
<NavigationContainer>
<Stack.Navigator
mode="modal"
>
<>
<Stack.Screen
name="User"
component={UserNavigator}
/>
</>
</Stack.Navigator>
</NavigationContainer>
)
}
})
}
The error I'm getting is:
Error: Objects are not valid as a React child (found: object with keys {_U, _V, _W, _X}). If you meant to render a collection of children, use an array instead.
Any help would be appreciated :)
Try doing it like this
const Display = () => {
const [toDisplay, setToDisplay] = useState(
<NavigationContainer>
<Stack.Navigator mode="modal">
<>
<Stack.Screen name="User" component={UserNavigator} />
</>
</Stack.Navigator>
</NavigationContainer>,
);
useEffect(() => {
firebase
.auth()
.currentUser.getIdTokenResult()
.then((tokenResult) => {
if (tokenResult.claims.admin) {
setToDisplay(
<NavigationContainer>
<Stack.Navigator mode="modal">
<Stack.Screen name="Admin" component={AdminNavigator} />
</Stack.Navigator>
</NavigationContainer>,
);
}
});
}, []);
return toDisplay;
};
export default function UINav() {
return <Display />;
}
Don't forget to import useEffect & useState from React.

Categories

Resources