I'm new to react-native. I use react-native-side-menu to create a drawer and I add a bottom on the left side to skip to another page. when I push the bottom, the error code appeared. However, if I put the bottom in the homepage, it works. Why if I put it in the drawer it will crash?
This is route stack
App.js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { createStackNavigator, createAppContainer } from 'react-navigation';
import HomeScene from './homeScene';
import LoginScene from './loginScene';
import RegisterScene from './registerScene';
import TimetableScene from './timetable';
import ChatScene from './ChatScene';
import LeftMenu from './LeftMenu';
const SimpleApp = createStackNavigator({
Login: {
screen: LoginScene,
navigationOptions: {
headerTitle: 'Login',
}
},
Home: {
screen: HomeScene,
navigationOptions: {
header: null,
}
},
Register: {
screen: RegisterScene,
navigationOptions: {
headerTitle: 'Register',
}
},
Timetable: {
screen: TimetableScene,
navigationOptions:{
headerTitle: 'Timetable',
}
},
//The page I want to skip
Chat: {
screen: ChatScene,
navigationOptions:{
headerTitle: 'Chat',
}
}
LeftMenu:{
screen: LeftMenu
}
});
const AppContainer = createAppContainer(SimpleApp);
export default class App extends React.Component {
render() {
return <AppContainer />;
}
}
LeftScene.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
TouchableOpacity,
SectionList
} from 'react-native';
export default class LeftMenu extends Component {
constructor(props) {
super(props);
this.selectSideMenu = this.selectSideMenu.bind(this);
}
selectSideMenu() {
this.props.onSelectMenuItem();
}
Chat = () => {
const { navigate } = this.props.navigation;
navigate('Chat');
}
render() {
return (
<View style={styles.container}>
//The bottom to skip to "Chat" page but will respond error
<TouchableOpacity
onPress={this.Chat}
style={styles.button}>
<Text
style={styles.btText}>Chat</Text>
</TouchableOpacity>
</View>
);
}
}
I think maybe the wrong code from the following code in LeftScene.js
Chat = () => {
const { navigate } = this.props.navigation;
navigate('Chat');
}
The this.props can only get the value from the parent component. The parent component of LeftMenu is homeScene, homeScene has no navigation so it doesn't work. And because of App.js is parent component of homeScene, so if I put the skip bottom in homeScene it can work. But I don't know how to figure out it...
homeScene.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
TextInput,
View,
TouchableOpacity,
Dimensions
} from 'react-native';
let { width, height } = Dimensions.get('window');
import SideMenu from 'react-native-side-menu'
import Menu from './LeftMenu'
export default class LeftSideMenu extends Component {
constructor(props) {
super(props);
this.state = {
isOpen: false,
}
this.SelectMenuItemCallBack = this.SelectMenuItemCallBack.bind(this);
}
SelectMenuItemCallBack() {
this.setState({
isOpen: !this.state.isOpen,
})
}
SelectToOpenLeftSideMenu() {
this.setState({
isOpen: true,
})
}
Chat = () => {
const { navigate } = this.props.navigation;
navigate('Chat');
}
render() {
const menu = <Menu onSelectMenuItem={this.SelectMenuItemCallBack} />;
return (
<SideMenu
menu={menu}
isOpen={this.state.isOpen}
onChange={(isOpen) => {
this.setState({
isOpen: isOpen,
})
}}
menuPosition={'left'}
openMenuOffset={0.75 * width}
edgeHitWidth={200}
>
<View
style={styles.top}>
//The bottom to open the drawer
<TouchableOpacity
onPress={() => this.SelectToOpenLeftSideMenu()}
style={styles.Fbutton} >
<Text
style={styles.btText}>F</Text>
</TouchableOpacity>
</View>
//The bottom to skip to "Chat" page and works
<View style={styles.container}>
<TouchableOpacity
onPress={this.Chat}
style={styles.button}>
<Text
style={styles.btText}>Chat</Text>
</TouchableOpacity>
</View>
</SideMenu>
);
}
}
I expect the bottom to skip to "Chat" page on the homeScene can be put in the drawer
You are facing this error because your LeftScene.js is not a part of your stack just add LeftScene.js in SimpleApp.
It will work.
Just change the code in homeScene.js
const menu = <Menu onSelectMenuItem={this.SelectMenuItemCallBack} />;
to the following
const menu = <Menu onSelectMenuItem={this.SelectMenuItemCallBack} navigation={this.props.navigation} />;
Related
I would like to implement scrolling to top. My tab is a flatlist, and when users scroll down the flatlist, they have to scroll back up. Instagram and Twitter allow you to press the tab to scroll back up, I am wondering how to implement it in my own app.
Here is the tab I want to implement scrolling to top:
//Bottom Tabs
function Tabs() {
...
<Tab.Screen
name="Home"
component={globalFeedStackView}
options={{
tabBarLabel: ' ',
tabBarIcon: ({ color, size }) => (
<Ionicons name="ios-home" size={size} color={color} />
),
}}
/>
}
And the class component for the tab above:
class GlobalScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
globalPostsArray: [],
navigation: this.props.navigation,
};
}
async componentDidMount() {
this.getCollection()
Analytics.setUserId(Firebase.auth().currentUser.uid)
Analytics.setCurrentScreen("GlobalScreen")
}
...
render() {
return (
<View style={styles.view}>
<FlatList
data={this.state.globalPostsArray}
renderItem={renderItem}
keyExtractor={item => item.key}
contentContainerStyle={{ paddingBottom: 50 }}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
onRefresh={this._refresh}
refreshing={this.state.isLoading}
onEndReached={() => {this.getMore()}}
/>
<KeyboardSpacer />
</View>
)
}
According to react navigation I can do something like this:
import * as React from 'react';
import { ScrollView } from 'react-native';
import { useScrollToTop } from '#react-navigation/native';
class Albums extends React.Component {
render() {
return <ScrollView ref={this.props.scrollRef}>{/* content */}</ScrollView>;
}
}
// Wrap and export
export default function(props) {
const ref = React.useRef(null);
useScrollToTop(ref);
return <Albums {...props} scrollRef={ref} />;
}
But this solution is for a scrollview, and I am using a flatlist.
How can I implement pressing a tab to scroll to the top of my flatlist?
scrollToOffset
you can do it the same way with a ref on your FlatList :
import * as React from 'react';
import { FlatList } from 'react-native';
class Albums extends React.Component {
render() {
return <FlatList ref={this.props.scrollRef} />;
}
// Wrap and export
export default function(props) {
const ref = React.useRef(null);
ref.scrollToOffset({ animated: true, offset: 0 });
return <Albums {...props} scrollRef={ref} />;
}
I just created a React Native app with Expo using expo init. Everything went fine. Then I went on and created a Home screen like so:
import React from 'react';
import { View, TextInput } from 'react-native';
export default class HomeScreen extends React.Component {
state = {
searchText: ""
}
render() {
return (
<View
style={{
flex: 1,
backgroundColor: 'white'
}}
>
<TextInput
placeholder="Search..."
value={this.state.searchText}
onChangeText={ (searchText) => this.setState({ searchText }) }
/>
</View>
);
}
}
I added it to my AppNavigator...
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import HomeScreen from '../screens/HomeScreen';
const AppNavigator = createStackNavigator({
Home: HomeScreen
});
export default createAppContainer(AppNavigator);
...which I in turn added to App.js:
import React from 'react';
import { StyleSheet, View, StatusBar, Platform } from 'react-native';
import { AppLoading } from 'expo';
import AppNavigator from './navigation/AppNavigator';
export default class App extends React.Component {
state = {
isLoadingComplete: false
}
render() {
if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) {
return (
<AppLoading
startAsync={this._loadResourcesAsync}
onError={this._handleLoadingError}
onFinish={this._handleFinishLoading}
/>
);
}
return (
<View style={styles.container}>
{Platform.OS === 'ios' && <StatusBar barStyle="default" />}
<AppNavigator />
</View>
);
}
_loadResourcesAsync = async () => { };
_handleLoadingError = error => {
console.warn(error);
};
_handleFinishLoading = () => {
this.setState({ isLoadingComplete: true });
};
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Now, my problem is that my app looks like this:
As you can see, the horizontal margins are way too big, but I never set them to be like that. What am I doing wrong? I want the TextInput to stretch across the full screen.
Your HomeScreen Component will be as wide as its contents. You did not specify the width of the main View, therefore, it adjusted itself to be as wide the components inside. What you can do to resolve this issue is that you must provide width property in your Top level View's style.
And in order to get the Width of the screen, you can import Dimensions from react-native and use that like below:
const windowWidth = Dimensions.get('window').width;
and finally, you can assign the windowWidth to your view like so:
style={{flex:1, width: windowWidth, backgroundColor: 'white'}}
I'm trying to implement the react-navigation package to make one screen link to another. I'm getting an error "undefined is not an object (evaluating '_this2.props.navigation.navigate') - React Native"
I'm fairly sure this error is because I have nothing in the App.js part of my project.
I have a file called screen.js that has two classes (one for each screen), and an onpress function that calls the this.props.navigator to change screens.
Screen.js
import React, { Component } from 'react';
import {
StyleSheet,
Text,
TextInput,
View,
TouchableHighlight,
PanResponder,
AppRegistry,
Image,
Alert,
} from 'react-native';
import { Card, Button, Icon } from 'react-native-elements';
import { createStackNavigator, createAppContainer } from 'react-navigation';
import { Constants, MapView } from 'expo';
class HomeScreen extends Component {
static navigationOptions = {
title: 'Home',
};
constructor() {
super();
this.state = {...};
}
_handleButtonPress = () => {
Alert.alert('Button pressed!', 'You did it!');
};
componentWillMount() {
this.panResponderRef = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponder: () => true,
onPanResponderGrant: this.doNothing,
onPanResponderMove: this._handlePanResponderMove,
onPanResponderRelease: this._handlePanResponderEnd,
onPanResponderTerminate: this.doNothing,
});
}
//onPanResponderRelease and onPanResponderTerminate Handler
_handlePanResponderEnd = (event, gestureState) => {
const { navigate } = this.props.navigation;
let tIndex = this.state.index;
if (this.state.dy > 100)
navigate('Second', { index: tIndex, getFunc: this.getName.bind(this) });
else if (this.state.dx > 150) this.setState({ index: tIndex + 1 });
else if (this.state.dx < -150 && tIndex > 0)
this.setState({ index: tIndex - 1 });
};
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.container}>
<TouchableHighlight
style={styles.TouchableHighlight}
onPress={() => this.props.navigator.push({id:'SecondScreen'})}>
<View style={styles.author}>
<Text style={styles.author}>Shelter 1</Text>
</View>
</TouchableHighlight>
</View>
);
}
}
class SecondScreen extends React.Component {
static navigationOptions = {
title: 'Second',
};
constructor(props) {
super(props);
this.state = {
index: this.props.navigation.state.params.index,
getFunc: this.props.navigation.state.params.getFunc,
name: 'not set'
};
}
componentWillMount() {
let tName = this.state.getFunc(this.state.index);
this.setState({ name: tName });
}
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.container}>
</View>
<Button title="Go to Home page" onPress={() => navigate('Home')} />
</View>
);
}
}
const NavigationApp = createStackNavigator({
Home: { screen: HomeScreen },
Second: { screen: SecondScreen },
});
export default createAppContainer(NavigationApp);
});
App.js
import NavApp from "screen";
export default NavApp;
I am getting an error "undefined is not an object (evaluating '_this2.props.navigator.push')"
You could try this
this.props.navigation.push('SecondScreen')
I am trying to have a tab navigator, where the settings screen/tab is a component, that, among other stuff, contains a stack navigator.
Here is the code I currently have (can also be found as a snack).
import * as React from 'react';
import { Button, Text, View, StyleSheet } from 'react-native';
import { createAppContainer, createStackNavigator, createBottomTabNavigator } from 'react-navigation';
class ScreenA extends React.Component {
static navigationOptions = {
tabBarLabel: 'A',
};
render() {
return (
<View>
<Text>Screen A</Text>
</View>
);
}
}
class SettingsHome extends React.Component {
render() {
return (
<Button onPress={() => this.props.navigation.navigate('SettingsScreenA')}>
<Text>Navigate to Settings A</Text>
</Button>
);
}
}
class SettingsScreenA extends React.Component {
render() {
return (
<View>
<Text>Settings A</Text>
<Button onPress={() => this.props.navigation.navigate('SettingsA')}>
<Text>Back to SettingsHome</Text>
</Button>
</View>
);
}
}
const SettingsStackNavigator = createStackNavigator({
SettingsHome: { screen: SettingsHome },
SettingsScreenA: { screen: SettingsScreenA }
})
class SettingsScreen extends React.Component {
static navigationOptions = {
tabBarLabel: 'Settings',
}
render() {
return (
<View>
<Text>Some other components...</Text>
<SettingsStackNavigator navigation={this.props.navigation}/>
</View>
);
}
}
const RootTabNavigator = createBottomTabNavigator({
ScreenA: { screen: ScreenA },
Settings: {screen: SettingsScreen },
});
const Navigation = createAppContainer(RootTabNavigator);
export default class App extends React.Component {
render() {
return (
<Navigation />
);
}
}
I currently get a 'Type Error: No "routes" found in navigation state' error. Did you try to pass the navigation prop of a React component '
you'll need to nest the component itself in a stackNavigator, containing the all the stackScreen, like so;
import * as React from 'react';
import { Button, Text, View, StyleSheet } from 'react-native';
import { createAppContainer, createStackNavigator, createBottomTabNavigator } from 'react-navigation';
class ScreenA extends React.Component {
static navigationOptions = {
tabBarLabel: 'A',
};
render() {
return (
<View style={{alignItems:'center', justifyContent:'center', flex:1}}>
<Text>Screen A</Text>
</View>
);
}
}
class SettingsHome extends React.Component {
render() {
return (
<Button onPress={() => this.props.navigation.navigate('SettingsScreenA')}
title ="Navigate to Settings A"
/>
);
}
}
class SettingsScreenA extends React.Component {
render() {
return (
<View style={{alignItems:'center', justifyContent:'center', flex:1}}>
<Text>Settings A</Text>
<Button onPress={() => this.props.navigation.navigate('SettingsHome')}
title ="Back to SettingsHome"
/>
</View>
);
}
}
class SettingsScreen extends React.Component {
render() {
return (
<View style={{alignItems:'center', justifyContent:'center', flex:1}}>
<Text>Some other components...</Text>
<Button title="Seting A" onPress={() => this.props.navigation.navigate('SettingsScreenA')} />
</View>
);
}
}
// Create our stack navigator
const SettingsStackNavigator = createStackNavigator({
SettingsMain: {screen: SettingsScreen,},
SettingsHome: { screen: SettingsHome },
SettingsScreenA: { screen: SettingsScreenA }
}, {initialRouteName: 'SettingsMain'})
const RootTabNavigator = createBottomTabNavigator({
ScreenA: { screen: ScreenA },
Settings: {screen: SettingsStackNavigator },
});
// And the app container
export default createAppContainer(RootTabNavigator);
hope it helps!
I have an issue with react-navigation in passing the props to another screen,
I need to pass some props to Detail screen when the user presses one of the List Places I need to push screen with some details about the place like a Name of place and Image of place,
Errors :
this is my Code
Reducer
import { ADD_PLACE, DELETE_PLACE } from "../actions/actionTypes";
const initialState = {
places: []
};
import placeImage from "../../assets/img.jpg";
const reducer = (state = initialState, action) => {
switch (action.type) {
case ADD_PLACE:
return {
...state,
places: state.places.concat({
key: Math.random(),
name: action.placeName,
// image: placeImage,
image: {
uri:
"https://images.unsplash.com/photo-1530009078773-9bf8a2f270c6?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=750&q=80"
}
})
};
case DELETE_PLACE:
return {
...state,
places: state.places.filter(place => {
return place.key !== state.selectedPlace.key;
})
};
default:
return state;
}
};
export default reducer;
Place List Component
import React from "react";
import { FlatList, StyleSheet, ScrollView } from "react-native";
import ListItem from "../ListItem/ListItem";
const PlaceList = props => {
return (
<FlatList
style={styles.listContainer}
data={props.places}
renderItem={info => (
<ListItem
placeName={info.item.name}
placeImage={info.item.image}
onItemPressed={() => props.onItemSelected(info.item.key)}
/>
)}
keyExtractor={index => String(index)}
/>
);
};
export default PlaceList;
Find Place Screen
import React, { Component } from "react";
import { View, Text } from "react-native";
import { connect } from "react-redux";
import { StackActions } from "react-navigation";
import PlaceList from "../../Components/PlaceList/PlaceList";
class FindPlaceScreen extends Component {
constructor() {
super();
}
itemSelectedHandler = key => {
const selPlace = this.props.places.find(place => {
return place.key === key;
});
this.props.navigation.push("PlaceDetail", {
selectedPlace: selPlace,
name: selPlace.name,
image: selPlace.image
});
};
render() {
return (
<View>
<PlaceList
places={this.props.places}
onItemSelected={this.itemSelectedHandler}
/>
</View>
);
}
}
const mapStateToProps = state => {
return {
places: state.places.places
};
};
export default connect(mapStateToProps)(FindPlaceScreen);
Place Details Screen
import React, { Component } from "react";
import { View, Text, Image, TouchableOpacity, StyleSheet } from "react-native";
import Icon from "react-native-vector-icons/Ionicons";
class PlaceDetail extends Component {
constructor(props) {
super(props);
}
render() {
return (
<View style={styles.modalContainer}>
<View>
<Image
source={this.props.navigation.state.params.image}
style={styles.placeImage}
/>
<Text style={styles.placeName}>
{this.props.navigation.state.params.namePlace}
</Text>
</View>
<View>
<TouchableOpacity onPress={props.onItemDeleted}>
<View style={styles.deleteButton}>
<Icon size={30} name="ios-trash" color="red" />
</View>
</TouchableOpacity>
<TouchableOpacity onPress={props.onModalClosed}>
<View style={styles.deleteButton}>
<Icon size={30} name="ios-close" color="red" />
</View>
</TouchableOpacity>
</View>
</View>
);
}
}
export default PlaceDetail;
You need to use react-native-navigation v2 for the find place screen
itemSelectedHandler = key => {
const selPlace = this.props.places.find(place => {
return place.key === key;
});
Navigation.push(this.props.componentId, {
component: {
name: 'PlaceDetail',
options: {
topBar: {
title: {
text: selPlace.name
}
}
},
passProps: {
selectedPlace: selPlace
}
}
});
};
make sure you import { Navigation } from "react-native-navigation";
Your PlaceDetail has some error
<TouchableOpacity onPress={props.onItemDeleted}>
<TouchableOpacity onPress={props.onModalClosed}>
Change props to this.props
<TouchableOpacity onPress={this.props.onItemDeleted}>
<TouchableOpacity onPress={this.props.onModalClosed}>
But I don't see onItemDeleted and onModalClosed anywhere, don't forget to pass those to PlaceDetail via props as well :)