I created a simple Android app that changes navigates when the text is pressed. The app runs properly but when I touch the text, the contents do not change and no navigation is observed. You can have a look at the error here. I have also provided the code:
import React, { Component, PropTypes } from 'react';
import { Navigator, Text, TouchableHighlight, View, AppRegistry} from
'react-native';
export default class SimpleNavigationApp extends Component {
constructor(props){
super(props);
this.state={
title: 'My Initial Scene',
}
}
render() {
return (
<Navigator
initialRoute={{ title: 'My Initial Scene', index: 0 }}
renderScene={(route, navigator) =>
<MyScene
title={route.title}
// Function to call when a new scene should be displayed
onForward={ () => {
const nextIndex = route.index + 1;
navigator.push({
title: 'Scene ' + nextIndex,
index: nextIndex,
});
}}
// Function to call to go back to the previous scene
onBack={() => {
if (route.index > 0) {
navigator.pop();
}
}}
/>
}
/>
)
}
}
class dhrumil extends Component {
static propTypes = {
title: PropTypes.string.isRequired,
onForward: PropTypes.func.isRequired,
onBack: PropTypes.func.isRequired,
}
render() {
return (
<View>
<Text>Current Scene: { this.props.title }</Text>
<TouchableHighlight onPress={this.props.onForward}>
<Text>Tap me to load the next scene</Text>
</TouchableHighlight>
<TouchableHighlight onPress={this.props.onBack}>
<Text>Tap me to go back</Text>
</TouchableHighlight>
</View>
)
}
}
AppRegistry.registerComponent("dhrumil",()=>dhrumil);
As you can see in the error, the title is also not displayed after the text "My Current Scene: ". How can I solve this?
First, your export default class SimpleNavigationApp is never called.
You should put it in another js file and import it to dhrumil class
.
Second,
import { Navigator } from 'react-native' is no longer supported.
Read https://facebook.github.io/react-native/docs/navigation.html for detailed navigation documentation.
Related
How The App works!
I have an app that shows a list of names and after that shows the meaning of the Name depends on some preference the user choose it from a dropDowns.
So in total there is a Main of 3 Screens!
First: is the UI screen that asks user to choose data,
Second: is the screen that holds a list contains names, I list them using a FlatList (BTW the data retrives from a SqLite -not important-)
Third: is the Screen that shows the name's meaning,
I am navigating from the "Main-Screen" to the "NameList-Screen" then To the "NameMeaning-Screen",
The navigation to the "NameMeaning-Screen" done by pressing on an item from the List.
Till here the app working nicely!
I decided to add a new feature to the app and that to show all the names i have in my DB and show it in a screen that have two Tabs, The first tab shows male's names and the second tab shows the female's names.
I did that step too.
But What am facing now is!
I want to navigate from that tabs when pressing on an item from the flatList, and navigate to the "Third Screen" I mentioned above the name's meaning!
But it gave error that, there is no such screen called "NameMeaing" and then it says " Name screen Its not handled by any navigator", So as much as i understand, when am in the Tabs the program have no access to the stack Navigator and cuz of this it gives that error.
As much as i was able to find on Web, There is examples of navigating from tabs to another stack screens, but in the all the examples, the tabs were the main screen in the applicaiton, but in my case i reach the tabs after pressing a certain button and navigate to another stacked screen.
As a soluation for the problem i thought about creating a secondry stack navigator inside my Tabs.js file that contains the tabs but i couldnt, then i thought i should create a Tab navigator in my App.js adding it to my stack navigator that already exists there and compine them in a navigator container. Maybe this is the soluation but i couldnt complete the code and connect the dots. Any help please?!
This is a video of the app while working (The scenario)
https://youtu.be/dBLNF5zMCt0
This is the Error it shows:
Error when i try to navigate from a tab screen to another different screen
This is App.js file
import 'react-native-gesture-handler';
import React, {Component} from 'react';
import {createStackNavigator} from 'react-navigation-stack';
import {createAppContainer} from 'react-navigation';
import { createMaterialTopTabNavigator } from '#react-navigation/material-top-tabs';
import MainScreen from './MainScreen';
import NamesList from './NamesList';
import NameMeaning from './NameMeaning';
import NameListWithTabsAgirlAboy from './NameListWithTabsAgirlAboy';
const App = createStackNavigator(
{
MainScreen: {
screen: MainScreen,
navigationOptions: {
header: null,
},
},
NamesList: {
screen: NamesList,
navigationOptions: {
header: null,
},
},
NameListWithTabsAgirlAboy: {
screen: NameListWithTabsAgirlAboy,
navigationOptions: {
header: null,
},
},
NameMeaning: {
screen: NameMeaning,
navigationOptions: {
header: null,
},
},
},
{
initialRouteName: 'MainScreen',
}
);
export default createAppContainer(App);
This is NameMeaninng.js file
class NameMeaning extends Component {
constructor(props) {
super(props);
}
render() {
const {navigation} = this.props;
return(
<SafeAreaView style= {styles.container}>
<Text style={styles.title}>معنى اسم {JSON.stringify(navigation.getParam('nameTitle', 'NO-ID'))}</Text>
<ScrollView style={styles.scrollView}>
<Text style={styles.text}>
{JSON.stringify(navigation.getParam('explaination', 'NO-ID'))}
</Text>
</ScrollView>
</SafeAreaView>
);
}
}
this is parts of Tabs.js file
This file have three classes in totall. BoysScreen, GirlsScreen and Tabs classes..
class BoysScreen extends React.Component {
constructor(props) {
super(props);
const {navigation} = this.props;
}
render() {
let FlatListNames = [];
FlatListNames = boysNames();
const {navigation} = this.props;
function Item({ title }, {navigation}) {
return (
<View style = {StyleSheet.item}>
<Text styel = {styles.title}> {title} </Text>
<Text style ={StyleSheet.explain}>اضغط للشرح</Text>
</View>
)
}
function boysNames() {
var boysNamesList = [];
db.transaction(tx => {
// boys names
tx.executeSql('SELECT ID, Name, Explanation FROM Names WHERE NameSex=?', ["لـ طفلي"], (tx, results) => {
for (let i = 0; i < results.rows.length; ++i) {
boysNamesList.push(results.rows.item(i));
}
});
}); // DB transaction
return boysNamesList;
};
return(
<View style= {styles.container}>
<FlatList
data={FlatListNames}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) =>
<TouchableOpacity
onPress = {() => {this.props.navigation.navigate('NameMeaning',{
nameTitle : item.Name,
nameId : item.ID,
explaination : item.Explanation,
});
}}
>
<Item title = {item.Name}/>
</TouchableOpacity>
}
/>
</View>
);
}
}// ends of BoysScreen Class
class Tabs extends React.Component {
constructor(props){
super(props);
}
render() {
const Tab = createMaterialTopTabNavigator();
// I have tried to create a stack here but it gave errors and couldnt solve it
//cont Stack = createStackNavigator();
return(
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name ="FemaleNames" component = {GirlsScreen} /> //GirlsScreen is a class
<Tab.Screen name = "MaleNames" component = {BoysScreen} /> // BoysScreen is a class
</Tab.Navigator>
// I have tried to import NameMeanig class and navigate to it like this, but it gaves errors too.
//<Stack.Screen name="Home" component={NameMeaning} />
</NavigationContainer>
);
}
Thanks in advance, any help of how i can build this algorithm is appricated really..
It was a problem with version compatibility, i was using React navigation V4 and V5 alltogether, After Searching more i have solved it in this way, Changing App.js and compine all the stacks and tabs navigators in App.js
Credit to this guy : https://www.youtube.com/watch?v=nQVCkqvU1uE
import 'react-native-gesture-handler';
import React, {Component} from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createMaterialTopTabNavigator } from '#react-navigation/material-top-tabs';
import MainScreen from './App/components/Home/MainScreen';
import NamesList from './App/components/NameList/NamesList';
import NameMeaning from './App/components/NameMeaning/NameMeaning';
import GirlNamesScreen from './App/components/NameList/GirlsNamesTab';
import BoysNamesScreen from './App/components/NameList/BoysNamesTab';
const MainStack = createStackNavigator();
const MainStackScreen = () => (
<MainStack.Navigator>
<MainStack.Screen
name="MainScreen"
component={MainScreen}
options={{ title: "Main Screen title" }}
/>
<MainStack.Screen
name="NamesList"
component={NamesList}
options={{ title: "NamesList Screen title" }}
/>
<MainStack.Screen
name="NameMeaning"
component={NameMeaning}
options={{ title: "NameMeaning Screen title" }}
/>
<MainStack.Screen
name="TabsScreen"
component={TabsScreen}
options={{ title: "TabsScreen Screen title" }}
/>
</MainStack.Navigator>
);
const Tabs = createMaterialTopTabNavigator();
const GirlNamesStack = createStackNavigator();
const BoysNamesStack = createStackNavigator();
const GirlNamesStackScreen = () => (
<GirlNamesStack.Navigator>
<GirlNamesStack.Screen name="GirlsNames" component={GirlNamesScreen} />
</GirlNamesStack.Navigator>
);
const BoysNamesStackScreen = () => (
<BoysNamesStack.Navigator>
<BoysNamesStack.Screen name="BoysNames" component={BoysNamesScreen} />
</BoysNamesStack.Navigator>
);
const TabsScreen = () => (
<Tabs.Navigator>
<Tabs.Screen name="BoysNames" component={BoysNamesStackScreen} />
<Tabs.Screen name="GirlsNames" component={GirlNamesStackScreen} />
</Tabs.Navigator>
);
const RootStack = createStackNavigator();
const RootStackScreen = () => (
<RootStack.Navigator headerMode="none">
<RootStack.Screen
name="Main"
component={MainStackScreen}
options={{
animationEnabled: false
}}
/>
</RootStack.Navigator>
);
export default () => {
return (
<NavigationContainer>
<RootStackScreen/>
</NavigationContainer>
);
};
I would like to know how I would go about implementing a welcome/getting started screen using react navigation v3.
My confusion would be where the welcome/getting started screen would go?
should the screen be in the Appstack or Authstack?
I want to display this to new users only. When a user logs out and re-authenticate I want it to be popped out of the stack because they are not new users and take them directly to the main app.
I think this piece of logic should take place in the Authloadingscreen, am just not sure how or what technique to use.
This is an example Appfrom https://snack.expo.io/#react-navigation/auth-flow-v3
Any help would be appreciated, thanks.
import React from 'react';
import {
ActivityIndicator,
AsyncStorage,
Button,
StatusBar,
StyleSheet,
View,
} from 'react-native';
import { createStackNavigator, createSwitchNavigator, createAppContainer } from 'react-navigation';
class SignInScreen extends React.Component {
static navigationOptions = {
title: 'Please sign in',
};
render() {
return (
<View style={styles.container}>
<Button title="Sign in!" onPress={this._signInAsync} />
</View>
);
}
_signInAsync = async () => {
await AsyncStorage.setItem('userToken', 'abc');
this.props.navigation.navigate('App');
};
}
class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Welcome to the app!',
};
render() {
return (
<View style={styles.container}>
<Button title="Show me more of the app" onPress={this._showMoreApp} />
<Button title="Actually, sign me out :)" onPress={this._signOutAsync} />
</View>
);
}
_showMoreApp = () => {
this.props.navigation.navigate('Other');
};
_signOutAsync = async () => {
await AsyncStorage.clear();
this.props.navigation.navigate('Auth');
};
}
class OtherScreen extends React.Component {
static navigationOptions = {
title: 'Lots of features here',
};
render() {
return (
<View style={styles.container}>
<Button title="I'm done, sign me out" onPress={this._signOutAsync} />
<StatusBar barStyle="default" />
</View>
);
}
_signOutAsync = async () => {
await AsyncStorage.clear();
this.props.navigation.navigate('Auth');
};
}
class AuthLoadingScreen extends React.Component {
constructor() {
super();
this._bootstrapAsync();
}
// Fetch the token from storage then navigate to our appropriate place
_bootstrapAsync = async () => {
const userToken = await AsyncStorage.getItem('userToken');
// This will switch to the App screen or Auth screen and this loading
// screen will be unmounted and thrown away.
this.props.navigation.navigate(userToken ? 'App' : 'Auth');
};
// Render any loading content that you like here
render() {
return (
<View style={styles.container}>
<ActivityIndicator />
<StatusBar barStyle="default" />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});
const AppStack = createStackNavigator({ Home: HomeScreen, Other: OtherScreen });
const AuthStack = createStackNavigator({ SignIn: SignInScreen });
export default createAppContainer(createSwitchNavigator(
{
AuthLoading: AuthLoadingScreen,
App: AppStack,
Auth: AuthStack,
},
{
initialRouteName: 'AuthLoading',
}
));
I would put this in the AppStack, since it is part of your app content and not part of your authentication flow.
Additionally, you need a way to determine if it's a new user or a returning user. So either you store this information server side, or locally using AsyncStorage. The best approach would be to store this information server side, since a user can always get a new phone. So during loading (if authenticated) or authenticating you make sure you fetch that data and display/hide the welcome screen accordingly.
I'm actually working on single page navigation, tab navigation and Drawer navigation by using react native navigation.
After the drawer implementation is done, single page navigation is working fine but the whole application is getting crashed after clicking on a login button.
Take a look at the code that I have written :
app.js:
import { Navigation } from 'react-native-navigation';
import AuthScreen from './src/screens/Auth/Auth';
import SharePlaceScreen from './src/screens/SharePlace/SharePlace';
import FindPlaceScreen from './src/screens/FindPlace/FindPlace';
import SideDrawer from "./src/screens/SideDrawer/SideDrawer";
// register screens
Navigation.registerComponent('example.AuthScreen', () => AuthScreen);
Navigation.registerComponent('example.SharePlaceScreen', () => SharePlaceScreen);
Navigation.registerComponent('example.FindPlaceScreen', () => FindPlaceScreen);
Navigation.registerComponent('example.SideDrawer', () => SideDrawer);
// start a app
Navigation.startSingleScreenApp({
screen: {
screen:'example.AuthScreen',
title:"Login"
}
})
startMainTab.js
import { Navigation } from 'react-native-navigation';
import {Dimensions} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
const wDim = Dimensions.get('window');
const fixedWidth = Math.round(wDim.width * wDim.scale * 0.8);
const startTab = () =>{
Promise.all([
Icon.getImageSource("map",30),
Icon.getImageSource("share",30),
Icon.getImageSource("menu",30),
]).then(sources =>{
Navigation.startTabBasedApp({
tabs: [
{
screen:"example.AuthScreen",
label : "Find Place",
title : "Find Place",
icon : sources[0],
navigatorButtons:{
leftButton:[
{
icon:sources[2],
title:"Menu",
id:"sideDrawerToggle"
}
]
}
},
{
screen:"example.AuthScreen",
label : "Share Place",
title : "Share Place",
icon : sources[1],
navigatorButtons:{
leftButton:[
{
icon:sources[2],
title:"Menu",
id:"sideDrawerToggle"
}
]
}
}
],
drawer: {
left: {
screen:"example.SideDrawer"
}
}
});
})
}
export default startTab;
SideDrawer.js
import React, { Component } from 'react';
import {View,Text,Dimensions,StyleSheet} from 'react-native';
class SideDrawer extends Component{
render(){
return(
<View >
<Text
style={[styles.container,{width:Dimensions.get("window").width*0.8}]}>
On SideDrawer
</Text>
</View>
)
}
}
const styles = StyleSheet.create({
container:{
paddingTop:22,
backgroundColor:"white",
flex:1
}
})
export default SideDrawer;
sharePlace.js
import React,{ Component } from 'react';
import {View,Text} from 'react-native';
class SharePlaceScreen extends Component{
constructor(props){
super(props);
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
}
onNavigatorEvent = event => {
console.log(events)
if(event.type === "NavBarButtonPress"){
if(event.id === "sideDrawerToggle"){
this.props.navigator.toggleDrawer({
side: 'left'
})
}
}
}
render(){
return(
<View>
<Text>On Share place screen</Text>
</View>
);
}
}
export default SharePlaceScreen;
leftButton: [
{
icon: sources[2],
title: "Menu",
id: "sideDrawerToggle"
}
]
This should be ,
leftButtons: [
{
icon: sources[2],
title: "Menu",
id: "sideDrawerToggle"
}
]
Follow this link :
https://wix.github.io/react-native-navigation/#/adding-buttons-to-the-navigator.
Hope this helps you. :)
I recreated the Drawer Navigation following this code: https://github.com/mariodev12/react-native-menu-drawer-navigator
Everything works correctly but now I do not know how to handle the native button to go back .. I would like to always return to the previous page, but if you press twice in the home exit the app.
This is my Code:
App.js
import React from 'react';
import {StackNavigator} from 'react-navigation';
import DrawerStack from './src/stacks/drawerStack';
const Navigator = StackNavigator({
drawerStack: {screen: DrawerStack}
}, {
headerMode: 'none',
initialRouteName: 'drawerStack'
})
export default Navigator
drawerStack.js
import React from 'react'
import {StackNavigator, DrawerActions} from "react-navigation";
import {Text, View, TouchableOpacity} from 'react-native';
import Home from "../components/home";
import DrawerScreen from "./drawerScreen";
const DrawerNavigation = StackNavigator({
DrawerStack: {screen: DrawerScreen}
}, {
headerMode: 'float',
navigationOptions: ({navigation}) => ({
headerStyle: {
backgroundColor: 'rgb(255,45,85)',
paddingLeft: 10,
paddingRight: 10
},
title: 'Home',
headerTintColor: 'white',
headerLeft: <View>
<TouchableOpacity
onPress={() => {
if (navigation.state.isDrawerOpen === false) {
navigation.dispatch(DrawerActions.openDrawer());
} else {
navigation.dispatch(DrawerActions.closeDrawer());
}
}}>
<Text>Menu</Text>
</TouchableOpacity>
</View>
})
})
export default DrawerNavigation;
drawerScreen.js
import {DrawerNavigator} from 'react-navigation'
import Home from '../components/home';
import Login from '../components/login';
import Contacts from '../components/contacts';
import News from '../components/news';
const DrawerScreen = DrawerNavigator({
Home: {screen: Home},
Login: {screen: Login},
Contacts: {screen: Contacts},
News: {screen: News}
}, {
headerMode: 'none',
initialRouteName: 'Home'
})
export default DrawerScreen;
news.js "Example of one page"
import React from "react";
import {Text, View} from 'react-native';
export default class News extends React.Component {
render() {
return (
<View>
<Text> Here Leave the News!! </Text>
</View>
);
}
}
Now, how do I insert the back button in the header instead of the classic menu (DrawerStack) for only the 'News.js' page?
In Android you have to handle back button actions by yourself with BackHandler from react-native.
First of all
import { BackHandler } from 'react-native';
in ComponentDidMount add an event listener to listen for backpress:
componentDidMount() {
BackHandler.addEventListener("hardwareBackPress", this.onBackPress);
}
in ComponentwillUnmount make sure you remove the listener:
componentWillUnmount() {
BackHandler.removeEventListener("hardwareBackPress", this.onBackPress);
}
then
onBackPress = () => {
//inside here do what you want with single back button
}
Checkout this link too:
https://reactnavigation.org/docs/en/drawer-based-navigation.html
If you want to go back to previous Screen with back button drawer navigation isn't for you and you should try to use Stack Navigator.
You need create the button in your news screen too, like this.
import React from "react";
import {Text, View} from 'react-native';
export default class News extends React.Component {
static navigationOptions = ({navigation}) => {
return {
headerLeft: --- PUT HERE YOU CUSTOM BUTTON (Use navigation.goBack() in onPress)
}
}
render() {
return (
<View>
<Text> Here Leave the News!! </Text>
</View>
);
}
}
To make better, you can create a new screen with only your custom navigation options.
I want my apps to display what the user entered in the tag under it with Redux.
So this is my container:
const mapStateToProps = state => ({
text: state
})
const mapDispatchToProps = (dispatch) => ({
addToList: () => { dispatch({ type: 'ADD_LIST' }) },
})
export default connect(mapStateToProps, mapDispatchToProps)(TodoList)
Here is my component:
class TodoList extends Component {
render() {
return (
<View>
<TextInput
style={{height: 40, width: 300}}
placeholder="Type here to translate!"
onChangeText={(text) => this.props.text}
/>
<Button
title="Submit"
onPress={this.props.addToList}/>
<View>
<Text>{this.props.text}</Text>
</View>
</View>
)
}
}
export default TodoList;
Here is the Store:
export const todoList = (state = [], action = {}) => {
switch (action.type) {
case 'ADD_LIST':
return [
...state,
action.todo
];
default:
return state;
}
}
let storeTodoList = createStore(todoList);
export default storeTodoList;
So i'm trying to get the text entered, add it to a list stored in the store and then display it, but i have absolutely no clue how to do this...
You have a few things going on here...
Your onChangeText listener isn't doing anything. You need to capture the text entered into the component and send it to your dispatcher.
You need to include the new text passed in as part of your action creator.
mapStateToProps is responsible for taking the elements in application state and mapping it to the props to be made available to your component. For this example, your application state is pretty simple. It is just { text: 'SOME TEXT' }.
You need to create a Provider for your Redux store. It should work at the root level of your app.
Here are all of the parts:
App.js (the application controller where the Provider is created)
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import todoList from './actions/Reducer';
import { createStore } from 'redux';
import Root from './Root';
class App extends Component {
store = createStore(todoList);
render() {
return (
<Provider store={this.store}>
<Root/>
</Provider>
)
}
}
export default App;
Root.js
import React, { Component } from 'react';
import { View, TextInput, Button, Text } from 'react-native';
import { connect } from 'react-redux';
class Root extends Component {
render() {
const displayText = this.props.textList.join();
return (
<View>
<TextInput
style={{height: 40, width: 300}}
placeholder="Type here to translate!"
onChangeText={(text) => this.props.updateField(text)}
/>
<Button
title="Submit"
onPress={() => this.props.addToList(this.props.text)}/>
<View>
<Text>{displayText}</Text>
</View>
</View>
)
}
}
const mapStateToProps = state => ({
text: state.textField,
textList: state.list
});
const mapDispatchToProps = (dispatch) => ({
updateField: (newText) => { dispatch({ type: 'FIELD_CHANGE', text: newText })},
addToList: (text) => { dispatch({ type: 'ADD_LIST', text: text }) },
});
export default connect(mapStateToProps, mapDispatchToProps)(Root)
Reducer.js (controls your state object)
const INITIAL_STATE = {
textField: '',
list: []
};
export default todoList = (state = INITIAL_STATE, action = {}) => {
switch (action.type) {
case 'FIELD_CHANGE':
return {
...state,
textField: action.text
};
case 'ADD_LIST':
return {
textField: '',
list: [...state.list, action.text]
};
default:
return state;
}
};
EDIT-Changed example to add to a list. Note: This is not the proper way to show a list of items in RN. I'm just throwing the string into a Text field for the example. Use FlatList to properly show a list of items.