Props are undefind when using StackNavigator - javascript

I am new to React native and I have an issue with undefined props while using the StackNavigator component in React-Native.
I've spent some days looking an answer for my question, but didn't find a propper way to resolve an issue.
The issue is in passing props to child component, they are undefined, but, the tutorial i followed is not doing anything else except of these steps.
Here screens and code of my app
When hitting green button in my LoginScreen the expected result is navigation to my MainScreen, but the result is "props are undefined"
I would be very gratful if someone can help me to solve this problem and help me to figure out, where the problem is!
My code :
App.js file
import React from 'react';
import { StyleSheet, Text, View, StatusBar} from 'react-native';
import Login from './src/components/login/Login';
import {StackNavigator} from 'react-navigation';
import MainScreen from './src/components/main/MainScreen';
export default class App extends React.Component {
static navigationOptions = { title: 'Welcome', header: null };
render() {
return (
<AppNavigation />
);
}
}
const AppNavigation = StackNavigator({
LoginScreen : {screen : Login},
MainScreen : {screen : MainScreen},
})
const styles = StyleSheet.create({
container: {
backgroundColor: '#3498db',
alignItems: 'center',
justifyContent: 'center',
},
});
Login.js , this component describes my Login page. It also renders component LoginForm, where i'm describing InputText and TouchableOpacity where i have onPress method, which must navigate me to MainScreen
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
Image
} from 'react-native';
import LoginForm from './LoginForm'
export default class Login extends React.Component {
static navigationOptions = { title: 'Welcome', header: null };
render() {
return (
<View style={styles.container}>
<View style={styles.logoContainer}>
<Image
source = {require('../../../images/logo.png')}
style = {styles.logo}>
</Image>
<Text style = {styles.title}>Добро пожаловать!</Text>
<Text style = {styles.titleLower}>Введите код для авторизации</Text>
</View>
<View style = {styles.formContainer}>
<LoginForm/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex : 1,
backgroundColor : '#3493db'
},
logoContainer : {
flexGrow : 1,
alignItems : 'center',
justifyContent : 'center'
},
logo : {
width : 120,
height : 120
},
title : {
color : '#fff',
marginBottom : 10,
fontSize : 22
},
titleLower : {
color : '#fff',
marginTop : 5,
fontSize : 12
}
});
LoginForm.js
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
TextInput,
TouchableOpacity,
KeyboardAvoidingView,
StatusBar
} from 'react-native';
export default class LoginForm extends Component {
render() {
return (
<KeyboardAvoidingView
behavior="padding"
style={styles.container}>
<TextInput
placeholder = "ваш пароль"
placeholderTextColor = '#fff'
underlineColorAndroid = {'transparent'}
style = {styles.input}
secureTextEntry>
</TextInput>
<TouchableOpacity
style= {styles.buttonContainer}
onPress = {() =>
this
.props
.navigation
.navigate('MainScreen')}>
<Text style= {styles.buttonText}>Войти</Text>
</TouchableOpacity>
</KeyboardAvoidingView>
);
}
}
const styles = StyleSheet.create({
container: {
padding: 20,
},
input : {
backgroundColor : 'rgba(255, 255, 255, 0.2)',
height : 40,
marginBottom : 10,
color : '#FFF',
fontSize : 16
},
buttonContainer : {
backgroundColor : '#00B241',
paddingVertical : 10,
alignItems: 'center',
justifyContent: 'center',
marginBottom : 30
},
buttonText : {
textAlign : 'center',
color : '#fff'
}
});

I believe the issue is that LoginForm.js is nested inside of login.js.
So, when you call this.props.navigation.navigate('MainScreen'), loginForm.js has no idea what navigation is, because it belongs to Login.js.
I believe passing the navigation object to loginForm.js from login.js will solve this issue:
<LoginForm screenProps={{ navigation: this.props.navigation }} />

Pass navigation props to loginForm. It works for me
<LoginForm navigation={this.props.navigation} />

Related

Custom Header don't take full space in stack bar

So i made a custom header component and tried to put it in the stack bar title, but the view doesn't take the full space even when passing the width and height to 100%.i also tried flex :1 but that doesn't work also
// header.js
import React from 'react';
import {View,Text,Button,StyleSheet} from 'react-native'
//class Header extends React.Component {
//render(){
export default function Header(){
return (
<View style={styles.header}>
<View>
<Text style={styles.text}>MEDICLIC</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
header: {
width:'100%',
height:'100%',
flexDirection:'row',
alignItems:'center',
justifyContent:'center',
backgroundColor:'#3498db',
},
text:{
fontWeight: '600',
color:'#ecf0f1',
fontSize: 32,
}
});
//export default Header
this is my app.js code where i used also a drawer navigation,
// APP.js
import React from 'react';
import Header from './Components/Header'
import { createDrawerNavigator } from 'react-navigation-drawer'
import {createAppContainer } from 'react-navigation'
import {createStackNavigator } from 'react-navigation-stack'
import ConnectionScreen from './Screens/ConnectionScreen';
import AccueilScreen from './Screens/AccueilScreen';
import AboutScreen from './Screens/AboutScreen';
export default class App extends React.Component {
render(){
return (
<AppNavigator/>
);
}
}
const AppDrawerNavigator = createDrawerNavigator({
Accueil:{
screen: AccueilScreen
},
SeConnecter: {
screen:ConnectionScreen} ,
Apropos : {
screen: AboutScreen
},
});
const screens = {
Accueil:{
screen : AppDrawerNavigator
},
SeConnecter: {
screen:AppDrawerNavigator
} ,
Apropos : {
screen: AppDrawerNavigator
},
}
const AppStackNavigator = createStackNavigator(screens,{
defaultNavigationOptions:{
headerTitle: () => <Header/>,
headerTintColor :'#fff',
headerStyle :{
backgroundColor:'#fff',
height: 100,
},
}
});
const AppNavigator= createAppContainer(AppStackNavigator);
What i see on my android device
enter image description here
<View style={{ flexDirection: "row", justifyContent: 'center' ,backgroundColor: 'red' ,width:360 }}>
///... 3 views and text in them
</View>
I did this, hard coded the width to main view of component using in place of title. about width 360 takes complete width having equal width on both left and right end.
Please do mention if you find a better way to sort the things. Thanks in advance.
I came across this problem today also, but after searching for solution for more than a hour, I finally get a way around it. The problem was that react native doesn't allow full access on the way the header component works anymore, you can only put the component that you want, but not override the header.
Therefore, to configure the stack header to your preference, you have to configure the header from the stack headerStyle property from the options prop.
For mine, I configure the background from the stack headerStyle property so as to fill the whole header,
import { createStackNavigator } from "#react-navigation/stack";
import Home from "../screens/home";
import ReviewDetails from "../screens/reviewDetails";
import Header from "../shared/header";
const Stack = createStackNavigator();
export default function MyStack() {
return (
<Stack.Navigator>
<Stack.Screen
name="Home"
component={Home}
options={{
headerTitle: () => <Header />,
headerStyle: {
backgroundColor: "coral",
},
}}
/>
<Stack.Screen name="ReviewDetails" component={ReviewDetails} />
</Stack.Navigator>
);
}
Then I use the absolute position on the header component style to put it in the middle accordingly.
Note: percent are still not working for the absolute positioning, so you still have to use pixels.
import { StyleSheet, Text, View } from "react-native";
import { MaterialIcons } from "#expo/vector-icons";
const Header = () => {
return (
<View style={styles.cont}>
<MaterialIcons name="menu" size={30} />
<View>
<Text style={styles.text}>GameZone</Text>
</View>
</View>
);
};
const styles = StyleSheet.create({
cont: {
flex: 1,
position: "absolute",
left: 80,
flexDirection: "row",
justifyContent: "space-around",
alignItems: "center",
},
text: {
fontSize: 24,
fontFamily: "nunito-bold",
letterSpacing: 2,
},
});
export default Header;
Here is the preview from android mobile
preview of the code

Cannot read property 'containerColor' of undefined (Making reactnative custom component)

Im trying to create my own react native component when I ran into this problem.
Cannot read property 'containerColor' of undefined
Module AppRegistry is not a registered callable module (calling
runApplication)
I import this component to my app.js where i supply the props. I dont know what else to do.
import React from "react";
import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
import PropTypes from "prop-types";
export default class TreeViewBasic extends React.Component {
constructor(props) {
super();
}
render() {
<View
style={[
Styles.container,
this.props.selected ? Styles.oNBg : Styles.ofFBg
]}
>
<TouchableOpacity onPress={this.props.onPress}>
<View style={{ flex: 1 }}>
<Text style={this.props.selected ? Styles.oNColor : Styles.ofFColor}>
{this.props.name}
</Text>
</View>
</TouchableOpacity>
</View>;
}
}
export const Styles = StyleSheet.create({
container: {
flex: 4, //4 out of 5
elevation: 2,
alignSelf: "flex-end",
height: 40
},
ofFColor: {
color: "darkgray"
},
oNColor: {
color: "black"
},
ofFBg: {
backgroundColor: "gray"
},
oNBg: {
backgroundColor: this.props.containerColor
}
});
TreeViewBasic.defaultProps = {
selected: false,
containerColor: "white"
};
TreeViewBasic.propTypes = {
name: PropTypes.string.isRequired,
onPress: PropTypes.func,
selected: PropTypes.bool,
containerColor: PropTypes.string
};
What is wrong or lacking? Thank you in advance!
You are trying to access props outside of the context. Use StyleSheet.flatten to deal with dynamic styles.
import React from "react";
import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
import PropTypes from "prop-types";
export default class TreeViewBasic extends React.Component {
constructor(props) {
super();
}
render() {
const {containerColor} = this.props;
// dynamic style
const dynamicBG = StyleSheet.flatten([Styles.oNBg, {
backgroundColor: containerColor
}];
<View
style={[
Styles.container,
this.props.selected ? dynamicBG : Styles.ofFBg
]}
>
<TouchableOpacity onPress={this.props.onPress}>
<View style={{ flex: 1 }}>
<Text style={this.props.selected ? Styles.oNColor : Styles.ofFColor}>
{this.props.name}
</Text>
</View>
</TouchableOpacity>
</View>;
}
}
export const Styles = StyleSheet.create({
container: {
flex: 4, //4 out of 5
elevation: 2,
alignSelf: "flex-end",
height: 40
},
ofFColor: {
color: "darkgray"
},
oNColor: {
color: "black"
},
ofFBg: {
backgroundColor: "gray"
},
oNBg: {
backgroundColor: "transparent" // default color
}
});
TreeViewBasic.defaultProps = {
selected: false,
containerColor: "white"
};
TreeViewBasic.propTypes = {
name: PropTypes.string.isRequired,
onPress: PropTypes.func,
selected: PropTypes.bool,
containerColor: PropTypes.string
};

"Route 'HomeScreen' should declare a screen" occuring for one of my components and i dont know why

I have been trying to implement react-navigation to my project to very little success.
Every time i import a screen to the navigator it comes up as "undefined".
I have tried loading just the screen to the top level and it seems to work but when i put it inside the navigation it returns to being undefined.
Heres my code:
index.js:
import React, { Component } from 'react';
import Root from './navigator/router';
//import Home from './screens/Home';
export class App extends Component {
render() {
return <Root />;
}
}
Home.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Slider,
Image,
ListView,
ScrollView,
TouchableOpacity
} from 'react-native';
import HeatingSliderComponent from '../components/HeatingSliderComponent';
export default class Home extends Component {
_onPressDial = () => {
this.props.navigation.navigate('DialScreen');
};
render() {
return (
<View style={styles.container}>
{/* Navigation bar */}
<View style={styles.navBarOuter}>
<View style={styles.navBarInner}>
{/* item 1 */}
<TouchableOpacity flex = {1}>
<Image
//style={styles.button}
source={require('../../images/menu_logout.png')}
/>
</TouchableOpacity>
{/* item 2 */}
<Image
flex = {3}
style = {{resizeMode: 'contain'}}
source={require('../../images/background_text.png')}
/>
{/* item 3 */}
<TouchableOpacity flex = {1}>
<Image
//style={styles.button}
source={require('../../images/menu_about.png')}
/>
</TouchableOpacity>
</View>
</View>
<View style = {styles.oneFlexGap}/>
{/* Main Dial Button */}
<View style={styles.dialButton}>
<TouchableOpacity onPress={this._onPressDial}>
<Image
//figure out how to make this work and maybe look into making a custom wheel of some kind.
//probably by writing in native android
style={styles.button}
source={require('../../images/menu_edit_4.png')}
/>
</TouchableOpacity>
</View>
{/*<View style = {styles.oneFlexGap}/>*/}
{/* Vertical Slider */}
{/*<View style = {styles.vertSliderContainer}>
<HeatingSliderComponent
step={1}
minimumValue={0}
maximumValue={100}
value={0}
/>
</View>*/}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 5,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
navBarInner: {
flex: 1,
flexDirection: 'row',
height: '5%',
width: '100%',
alignItems: 'center',
},
navBarOuter: {
flex: 2,
alignItems: 'center',
},
vertSliderContainer: {
flex: 2,
alignItems: 'center',
},
dialButton: {
flex: 2,
alignItems: 'center',
},
button: {
flex: 1
},
oneFlexGap: {
flex: 1
},
});
AppRegistry.registerComponent('Home', () => Home);
router.js:
import React from 'react';
import { StackNavigator } from 'react-navigation';
import DialScreen from '../screens/DialScreen';
import Home from '../screens/Home';
export const Root = StackNavigator({
HomeScreen: {
screen: Home,
},
DialScreen: {
screen: DialScreen,
navigationOptions: {
title: 'DialScreen',
},
},
});
I have tried a multitude of things to try and get it to work including making the project again in a new location.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
UPDATE: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I am still facing these errors but now i am facing the error "Route 'HomeScreen' should declare a screen error.
With my last change i did this to the router.js
// import DialScreen from '../screens/DialScreen';
// import Home from '../screens/Home';
var DialScreen = require('../screens/DialScreen');
var Home = require('../screens/Home');
and then structured the objects like this
DialScreen: {
screen: DialScreen.DialScreen,
navigationOptions: {
title: 'DialScreen',
},
from these changes i seem to get this error instead.
Route 'DialScreen' should declare a screen. For example:
import MyScreen from './MyScreen';
...
DialScreen: {
screen: MyScreen,
}
<unknown>
C:\Users\Dell\Documents\DSMReact\node_modules\react-navigation\src\routers\validateRouteConfigMap.js:22:6
validateRouteConfigMap
C:\Users\Dell\Documents\DSMReact\node_modules\react-navigation\src\routers\validateRouteConfigMap.js:18:21
default
C:\Users\Dell\Documents\DSMReact\node_modules\react-navigation\src\routers\StackRouter.js:36:25
default
C:\Users\Dell\Documents\DSMReact\node_modules\react-navigation\src\navigators\StackNavigator.js:44:29
loadModuleImplementation
C:\Users\Dell\Documents\DSMReact\node_modules\react-native\packager\src\Resolver\polyfills\require.js:174:12
guardedLoadModule
C:\Users\Dell\Documents\DSMReact\node_modules\react-native\packager\src\Resolver\polyfills\require.js:119:45
_require
C:\Users\Dell\Documents\DSMReact\node_modules\react-native\packager\src\Resolver\polyfills\require.js:110:24
_accept
C:\Users\Dell\Documents\DSMReact\node_modules\react-native\packager\src\Resolver\polyfills\require.js:274:12
<unknown>
C:\Users\Dell\Documents\DSMReact\node_modules\react-native\Libraries\Utilities\HMRClient.js:121:27
onmessage
C:\Users\Dell\Documents\DSMReact\node_modules\react-native\Libraries\Utilities\HMRClient.js:101:26
dispatchEvent
C:\Users\Dell\Documents\DSMReact\node_modules\event-target-shim\lib\event-target.js:172:43
<unknown>
C:\Users\Dell\Documents\DSMReact\node_modules\react-native\Libraries\WebSocket\WebSocket.js:148:27
emit
C:\Users\Dell\Documents\DSMReact\node_modules\react-native\Libraries\EventEmitter\EventEmitter.js:182:12
__callFunction
C:\Users\Dell\Documents\DSMReact\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:245:47
<unknown>
C:\Users\Dell\Documents\DSMReact\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:101:26
__guard
C:\Users\Dell\Documents\DSMReact\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:213:6
callFunctionReturnFlushedQueue
C:\Users\Dell\Documents\DSMReact\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:100:17
error message
Change from this in the index.js file
import Root from './navigator/router';
to
import { Root } from './navigator/router';
and this is because you are doing a named export on the Root StackNavigator.
------------------------------ OR -------------------------------
Instead of the previous solution, you change the export of the Root in the router.js file to a default export like so
const Root = StackNavigator({...});
at the bottom, you place the default export statement
export default Root;
Cheers!

understanding "undefined is not an object('this.props.navigation.navigate)"

I am getting the error "undefined is not an object('this.props.navigation.navigate)" when I click the button titled with "Chat with Lucy" which is supposed to take me to the ChatScreen screen.
All of this code is within the App.js file i'm using that is being exported into the android and ios files.
Any reason why i'm getting this error? thanks!
import React, { Component } from 'react';
import { StackNavigator } from 'react-navigation';
import {
AppRegistry,
StyleSheet,
Text,
View,
Image,
TextInput,
Button
} from 'react-native';
export default class firstapp extends Component {
static navigationOptions = {
title: 'Welcome',
};
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.container}>
<Image source={require('./Packit_title.png')} />
<TextInput
style={styles.account}
/>
<TextInput
style={styles.account}
/>
<View style={styles.button}>
<Button
title="Login"
color="#c47735"
/>
<Button
title="Sign Up"
color="#c47735"
/>
</View>
<Button
onPress={() => navigate('Chat')}
title="Chat with Lucy"
/>
</View>
);
}
}
class ChatScreen extends Component {
static navigationOptions = {
title: 'Chat with Lucy',
};
render() {
return (
<View>
<Text>Chat with Lucy</Text>
</View>
);
}
}
const firstappNav = StackNavigator({
Home: { screen: firstapp },
Chat: { screen: ChatScreen },
});
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f49542',
},
account: {
backgroundColor: '#ffffff',
height: 40,
borderColor: 'gray',
borderWidth: 1,
marginBottom: 10,
width: 200
},
button: {
flexDirection: 'row',
}
});
AppRegistry.registerComponent('firstapp', () => firstapp);
You are exporting your firstapp component which has no access to the navigation prop since nothing is being passed to it. You need to export your navigator component firstappNav instead.
AppRegistry.registerComponent('firstapp', () => firstappNav);
This is because props object is undefined in firstapp Component. You will have to override its constructor to access props. Read this

How to render a loader until data is fetched in React Native

I am fetching data through an async request. I know that I need to wait for the api request to complete before displaying the data. Unfortunately, I'm not sure how to create a loader to wait for the data to load.I am new to react, so if I could also get help with implementing it as well, that would be fantastic! Here is my current code:
import React, { Component, PropTypes } from 'react';
import { View, Text, ListView, StyleSheet, TouchableHighlight} from 'react- native';
import Header from '../Components/Header';
import Api from '../Utility/Api';
export default class CalendarPage extends Component {
constructor(props) {
super(props);
}
async componentWillMount() { this.setState(
{data: await Api.getDates()},
)
}
render() {
return (
<View style={{flex: 1}}>
<Header pageName="Calendar" navigator={this.props.navigator}/>
<View style = {{flex:9}}>
<View>
{ this.state.data.days[0].items.map((item) => (
<View>
<Text>{item.summary}</Text>
<Text>{item.start.dateTime}</Text>
<Text>{item.description}</Text>
</View>
))}
</View>
</View>
</View>
);
}
}
A simple example using ActivityIndicator -
import ActivityIndicator
import { View, Text, ListView, StyleSheet, TouchableHighlight, ActivityIndicator} from 'react- native';
set data state to null
constructor(props) {
super(props);
this.state = {
data: null
}
}
do conditional rendering
render() {
if (!this.state.data) {
return (
<ActivityIndicator
animating={true}
style={styles.indicator}
size="large"
/>
);
}
return (
<View style={{flex: 1}}>
<Header pageName="Calendar" navigator={this.props.navigator}/>
....
....
</View>
);
}
}
indicator style
const styles = StyleSheet.create({
indicator: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
height: 80
}
});
Although solution proposed by #vinayr works fine but user will still be able to click on screen and perform some action even while loader is being shown which can lead to crash.
One solution is wrap loader inside a Modal.
import React, { Component } from 'react';
import {
StyleSheet,
View,
Modal,
ActivityIndicator,
} from 'react-native';
const styles = StyleSheet.create({
modalBackground: {
flex: 1,
alignItems: 'center',
flexDirection: 'column',
justifyContent: 'space-around',
backgroundColor: '#00000040',
},
activityIndicatorHolder: {
backgroundColor: '#FFFFFF',
height: 100,
width: 100,
borderRadius: 10,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-around',
},
});
const SmartLoader = (props) => {
const {
isLoading,
...attributes
} = props;
return (
<Modal
transparent
animationType={'none'}
visible={isLoading}
onRequestClose={() => { console.log('Noop'); }}
>
<View style={styles.modalBackground}>
<View style={styles.activityIndicatorHolder}>
<ActivityIndicator
animating={isLoading}
size="large"
/>
</View>
</View>
</Modal>
);
};
export default SmartLoader;
After that you can use it anywhere in your component, user will not be able to perform any action till loader is finished ( made hidden based on flag)

Categories

Resources