Every little help is appreciated very much!
I got an Avatar as a Navigator, which should allow me to navigate through some screens! Some other Screens should be reached only by the my home screen. But when I press on the Button in the Avatar, nothing happens. I don't understand how and why, but I believe I made a mistake in the stackNavigator somewhere? Just to avoid any misunderstanding, the code below is shortened to avoid filling the page with unnecessary information, so it doesn't reflect 1:1 what you see in the screenshots.
Home Screen which should change
Avatar Screen through which I should be able to change the Home Screen
The goal is to be able to change which Screen is displayed in the <AppContainer/> through the <Avatar/> component below.
I tried with a onPress function inside the Avatar.js to call this.props.navigation.navigate("Agenda") but the screen didn't change. At least I got rid of the errors thanks to passing the navigation prop. I believe I made kind of a scrub mistake since i'm not experienced with react native, I hope some of the more experienced users can help me with that :) furthermore I don't think the screen itself is important to solve this, it's just a class component with panresponder and animated view.
import React from "react";
import { StyleSheet, View, StatusBar } from "react-native";
import { createAppContainer } from "react-navigation";
import { createStackNavigator } from "react-navigation-stack";
import Avatar from "../coreComponents/Avatar";
import ScHome from "../screens/ScHome";
import ScNewMedi from "../screens/ScNewMedi";
import ScCalendar from "./ScAgenda";
const AppStack = createStackNavigator(
{
Home: {
screen: ScHome,
},
Medis: {
screen: ScNewMedi,
},
Agenda: {
screen: ScCalendar,
},
},
{
headerMode: "screen",
initialRouteName: "Home",
backBehavior: "order",
}
);
const AppContainer = createAppContainer(AppStack);
export default class ScDefault extends React.Component {
render() {
return (
<View style={styles.container}>
<StatusBar
barStyle="light-content"
backgroundColor="transparent"
translucent
/>
<AppContainer />
<Avatar navigation={this.props.navigation} />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
The problem is you are trying to access navigation outside the Appcontainer where the navigation is not available.
Easy fix :
Moving the Avatar component inside one of your main screens(ScHome,ScNewMedi,ScCalendar) then you will have access to navigation and everything will work as expected.
Workaround :
Let say the requirement is to have the Avatar outside the AppContainer then you have to use something like a navigation service.
The code would be as below
let _navigator;
const setTopLevelNavigator=(navigatorRef) =>{
_navigator = navigatorRef;
}
const navigate=(routeName, params) =>{
_navigator.dispatch(
NavigationActions.navigate({
routeName,
params,
})
);
}
//Change your app.js
return <View style={{flex:1}}>
<AppContainer
ref={navigatorRef => {
setTopLevelNavigator(navigatorRef);
}}
/>
<Avatar navigate={navigate} /></View>;
//Inside the Avatar onPress you can can do this
onPress={()=>this.props.navigate("Home")}
You can refer the docs for more details
Related
I'm trying to make a RN-app, but I dont want to show the navigation on every screen. For example the registration and login screen shouldn't show the navigation. So I wanted to put the navigation in a component and import that component into the screens that should show it. I'm not sure if that works with navigation but I couldn't find anything saying that it won't work. I could be doing completely wrong.
The component I tried to create:
import React, { Component } from 'react'
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
class Navigation extends Component {
render() {
<Tab.Navigator>
<Tab.Screen name="Home" component={}/>
<Tab.Screen name="Explore" component={}/>
<Tab.Screen name="Account" component={}/>
</Tab.Navigator>
}
}
export default Navigation;
The way I wanted to import the component:
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Navigation } from './components';
export default function App() {
return (
<View style={styles.container}>
<Navigation/>
</View>
);
}
I ever had the same issue. And this react navigation documentation is very helpful.
Please check here https://reactnavigation.org/docs/auth-flow/
If you just want keep the navigation and just need to hide the tab bar please read this one https://reactnavigation.org/docs/hiding-tabbar-in-screens
I am developing mobile app using React Native expo.. I am getting the following exception:
Invalid hook call. Hooks can only be called inside the body of a function component....
I have gone through the other answers posted here on SO and have confirmed that the hook in my code is indeed inside a function. But still I am unable to resolve the error. KIndly help. Please see my code below. Let me know if more clarification is needed.
import React, { useState, useEffect } from 'react';
import { SafeAreaView, StatusBar, Button, View, Platform , Text} from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import Constants from 'expo-constants';
const statusBarPadding = Platform.OS === 'android' ? StatusBar.currentHeight: 0;
export default function OpenGallery () {
const [image, setImage] = useState(null);
useEffect(() => { // hook is inside function but still getting error
(async () => {
if (Platform.OS !== 'web') {
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== 'granted') {
alert('Sorry, we need camera roll permissions to make this work!');
}
}
})();
}, []);
return (
<SafeAreaView style={{ paddingTop: statusBarPadding }}>
<Text> Some text </Text>
</SafeAreaView>
);
}
Second file:
import React from 'react';
import { SafeAreaView, StatusBar, Button, View, Platform , Text} from 'react-native';
import OpenGallery from './OpenGallery'
const statusBarPadding = Platform.OS === 'android' ? StatusBar.currentHeight: 0;
export default function CameraScreen() {
return (
<SafeAreaView style={{ paddingTop: statusBarPadding }}>
<Text> Upload image from gallery.</Text>
<Button title="Select from Gallery" onPress={OpenGallery} />
</SafeAreaView>
);
}
it is in fact not being called from inside the component. you are calling it from the JSX, which is entirely different. there are two issues here that break the rules of hooks.
hooks must be called unconditionally. you are breaking that rule here.
<Button title="Select from Gallery" **onPress={OpenGallery}** />
Hooks must be called from inside a functional component. you cannot import another component and call it as a function. this is what you're doing. you are calling the react component on a onPress method which is wrong.
What can you do to fix it?
bring the state down. make the check to see if it's on a web or a mobile in the second file itself. i would post the code but i don't have expo installed at the moment.
It might sound out of scope here but I think in your case, you need to specify the behaviour on how do you want the OpenGallery to appear after pressing the button on CameraScreen... you may be using navigation (may be react-navigation) or using a modal.
Let's say you're using react-navigation. (as you're using expo, it's normally included in the project) https://reactnavigation.org/
On CameraScreen
export default function CameraScreen({navigation}) {
const gotoOpenGallery = () => {
navigation.navigate('OpenGallery');
}
return (
<SafeAreaView style={{ paddingTop: statusBarPadding }}>
<Text> Upload image from gallery.</Text>
<Button title="Select from Gallery" onPress={gotoOpenGallery} />
</SafeAreaView>
);
}
You will also need to create a StackNavigator
Your App.js (or your entry point of the app)
import * as React from 'react';
import { View, Text } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import OpenGallery from './OpenGallery';
import CameraScreen from './CameraScreen';
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="CameraScreen" component={CameraScreen} />
<Stack.Screen name="OpenGallery" component={OpenGallery} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
Im stuck with a Nested Navigation System. I have a main navigator (switchNavigator) and one of its screens is a component witch inside has some views and inside of one of this views I want to put another navigator (switchNavigator), but I get this error "The navigation prop is missing for this navigator. In react-navigation v3 and v4 you must set up your app container directly.". I really don't know if it's posible or a valid implementation.
This is my mainNavigator and two simple components, the second one is where I call the nested navigator:
import React from 'react';
import {createAppContainer,createSwitchNavigator} from 'react-navigation';
import { View, Text } from 'react-native';
import NestedNav from './nestedNav';
const FirstView = props=>{
return(
<View>
<Text>First view</Text>
</View>
)
}
const secondView = props=>{
return(
<View>
<View>
<Text>Second view</Text>
</View>
<View>
<NestedNav></NestedNav>
</View>
<View>
<Text>some other ui content</Text>
</View>
</View>
)
}
const MainNavigator = createSwitchNavigator(
{
firstView:FirstView,
secondView:secondView
},
{
initialRouteName: 'secondView',
}
);
export default createAppContainer(MainNavigator)
And this is my NestedNavigator:
import React from 'react';
import {createAppContainer,createSwitchNavigator} from 'react-navigation';
const NestedNav = createSwitchNavigator(
{
firstView:SomeFirstViewInNestedNav,
secondView:SomesecondViewInNestedNav
},
{
initialRouteName: 'firstView',
}
);
export default NestedNav
as you rendered the nestedNavigator it should be in wrapped into a appContainer.
const NestedNav = createSwitchNavigator(
{
firstView:SomeFirstViewInNestedNav,
secondView:SomesecondViewInNestedNav
},
{
initialRouteName: 'firstView',
}
);
--> export default createAppContainer(NestedNav)
I got the solution, I just had to create a static reference of the nested Navigation's Router in my nested navigator container component (in the example "secondView" component):
static router = NestedNav.router
And set the navigation prop to the nested navigator:
<NestedNav navigation = {this.props.navigation}></NestedNav>
This solution is in the Docs: here
I have been working on this problem for two days now and nothing on the web seems to be exactly what I am looking for.
I am attempting to implement a StackNavigator into my React Native app, but for some reason "navigation" is not being passed as a prop to the involved components. Therefore when I call this.props.navigation.navigator by pressing Button, I get the error undefined is not an object (evaluating this.props.navigation.navigate).
I have logged the props several times and the props object is empty, so the issue is not a deconstruction-of-the-props-object issue like others who get this error have had, but the fact that the navigation prop is not there in the first place.
I've tried placing the navigator code in its own file and in the App.js file thinking that it was somehow called after the components are rendered, and therefore not getting a chance to pass the navigation prop in, but that didn't work either. I've also looked to see if it is part of the props in the componentDidMount event. Still not.
import React, { Component } from 'react'
import { Text, View, Button, StyleSheet, FlatList } from 'react-native'
import { StackNavigator } from 'react-navigation'
import { getDecks } from '../utils/api'
import NewDeckView from './NewDeckView'
import DeckListItem from './DeckListItem'
export default class DeckListView extends Component {
constructor(props){
super(props)
this.state = {
decks: []
}
}
componentDidMount(){
console.log('props now test',this.props)
getDecks()
.then( result => {
const parsedResult = JSON.parse(result);
const deckNames = Object.keys(parsedResult);
const deckObjects = [];
deckNames.forEach( deckName => {
parsedResult[deckName].key = parsedResult[deckName].title
deckObjects.push(parsedResult[deckName])
})
this.setState({
decks:deckObjects
})
} )
}
render(){
return (
<View style={styles.container}>
<Text style={styles.header}>Decks</Text>
<FlatList data={this.state.decks} renderItem={({item})=><DeckListItem title={item.title} noOfCards={item.questions?item.questions.length:0}/>} />
<Button styles={styles.button} title="New Deck" onPress={()=>{this.props.navigation.navigate('NewDeckView')}}/>
</View>
)
}
}
const styles = StyleSheet.create({
header:{
fontSize:30,
margin:20,
},
container:{
flex:1,
justifyContent:'flex-start',
alignItems:'center'
},
button:{
width:50
}
})
const Stack = StackNavigator({
DeckListView : {
screen: DeckListView,
},
NewDeckView: {
screen:NewDeckView,
}
})
Like Vicky and Shubhnik Singh mentioned, you need to render the imported navigation stack in App.js like so:
import React from 'react';
import { Stack } from './navigator/navigator'
export default class App extends React.Component {
render() {
return <Stack/>
}
}
The navigator should look something like this and the first key in the object passed to StackNavigator will be rendered by default. In this case, it will be DeckListView.
import { StackNavigator } from 'react-navigation'
import DeckListView from '../components/DeckListView'
import NewDeckView from '../components/NewDeckView'
export const Stack = StackNavigator({
DeckListView : {
screen: DeckListView,
navigationOptions: {
headerTitle: 'Home',
},
},
NewDeckView: {
screen:NewDeckView,
navigationOptions: {
headerTitle: 'New Deck',
},
},
})
Thanks guys for the support! Somehow this wasn't clear for me in the documentation.
I am currently working on a app which works with react native and I tried to make a flow using react-navigation working on this tutorial but I am having trouble at the point of running my project, I've done a lot of research and i just cant get to the solution, first for all I am using react-native-cli: 2.0.1 and react-native: 0.48.3, this is my code:
App.js:
import React, { Component } from 'react';
import { Text, Button, View } from 'react-native';
import {
StackNavigator,
} from 'react-navigation';
class App extends Component {
static navigationOptions = {
title: 'Welcome',
};
render() {
console.log(this.props.navigation);
const { navigate } = this.props.navigation;
return (
<View>
<Text>Go to maps!</Text>
<Button
onPress={() => navigate('Map')}
title="MAP"
/>
</View>
);
}
}
export default App;
my Router.js
import {
StackNavigator,
} from 'react-navigation';
import MapMarker from './MapMarker';
import App from './App';
export const UserStack = StackNavigator({
ScreenMap:
{
screen: MapMarker,
navigationOptions:
{
title: "Map",
header:null
},
},
ScreenHome:
{
screen: App,
navigationOptions:
{
title: "Home",
headerLeft:null
},
},
});
As you can see this is a pretty basic App which i just cant make work, this is a screenshot of my error on the simulator:
I would really really appreciate if you guys could help me with this.
Thanks.
You should change the code onPress={() => navigate('Map')} to onPress={() => navigate('ScreenMap')} and ScreenHome should be the first screen then ScreenMap as you want to navigate from App to MapMarker. Or you can set initialRouteName: ScreenHome in the stacknavigator.
You create your StackNavigator, but where do you use it? You should have something like
import React, { Component } from 'react';
import {
AppRegistry,
View
} from 'react-native';
import {
StackNavigator,
} from 'react-navigation';
export default class MyApp extends Component {
render() {
return (
<View style={{flex:1}}>
<StackNavigator/>
</View>
);
}
}
AppRegistry.registerComponent('MyApp', () => MyApp);
Now that your StackNavigator is controlling what is shown, your App component will have navigation in its props. Note, you do not "pass" the navigation prop. This is handled for you.