Navigation in React Native - javascript

I'm having a problem with the Navigator in React Native. I want to navigate to another screen when pressing some Text, but I'm getting a strange error, I'm not too sure why.
Here's my code blocks and a picture of the error I'm receiving.
'use strict'
import React, { Component } from 'react';
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';
import ViewContainer from '../components/ViewContainer';
import StatusBarBackground from '../components/StatusBarBackground';
import AppNavigator from '../navigation/AppNavigator'
import UserIndexScreen from './UserIndexScreen'
class LoginIndexScreen extends Component {
render() {
return (
<ViewContainer>
<StatusBarBackground />
<View style={styles.textContainer}>
<Text style={styles.loginText}>Welcome to</Text>
<TouchableOpacity onPress={(event) => this._navigateToUserIndexScreen()}>
<Text style={styles.nextStep}>Press to go to User Index Screen</Text>
</TouchableOpacity>
</View>
</ViewContainer>
);
}
_navigateToUserIndexScreen() {
AppNavigator.props.push({
ident: "UserIndex"
})
}
}
const styles = StyleSheet.create({
textContainer: {
flex: 1,
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
loginText: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
marginTop: 30
},
nextStep: {
marginTop: 80
}
});
module.exports = LoginIndexScreen;
And here's the Navigator component.
'use strict'
import React, { Component } from 'react';
import { Navigator } from 'react-native';
import RegisterIndexScreen from '../screens/RegisterIndexScreen';
import LoginIndexScreen from '../screens/LoginIndexScreen';
import UserIndexScreen from '../screens/UserIndexScreen';
import PersonProfileScreen from '../screens/PersonProfileScreen';
class AppNavigator extends Component {
_renderScene(route, navigator) {
var globalNavigatorProps = { navigator }
switch(route.ident) {
case "RegisterIndexScreen":
return (
<RegisterIndexScreen {...globalNavigatorProps} />
)
case "LoginIndexScreen":
return (
<LoginIndexScreen {...globalNavigatorProps} />
)
case "UserIndex":
return (
<UserIndexScreen {...globalNavigatorProps} />
)
case "Temp":
return (
<Text>Hello</Text>
)
case "PersonProfileScreen":
return (
<PersonProfileScreen {...globalNavigatorProps}
person={route.person} />
)
default:
return (
<LoginIndexScreen {...globalNavigatorProps} />
)
}
}
render() {
return (
<Navigator
initialRoute={this.props.initialRoute}
ref="appNavigator"
renderScene={this._renderScene}
configureScene={(route) => ({...route.sceneConfig || Navigator.SceneConfigs.FloatFromRight, })} />
);
}
}
module.exports = AppNavigator;
Also here's a picture of the error I'm receiving:
Any ideas would be greatly appreciated!
Thanks!

You are trying to invoke a child component method in parent.
Method you are trying to invoke is not a part of your child component, you are calling RN Navigator method.
You should use navigator object that you passed to view in navigator renderScene function.
So in general you want to do sth. like:
In AppNavigator's _renderScene function pass navigator object:
<LoginIndexScreen nv={navigator} />
Then in LoginIndexScreen's _navigateToUserIndexScreen function use passed nv props:
this.props.nv.push({ ident: "UserIndex" })
You should also bind functions. Check https://facebook.github.io/react/docs/reusable-components.html#no-autobinding

Related

TypeError: render is not a function. (In 'render(newValue)', 'render' is an instance of Object)

import{ React, useContext} from 'react';
import { Button, View, Text } from 'react-native';
import { NavigationContainer, useNavigation ,useRoute } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import { LoginContexts } from '../Contexts/LoginContexts';
function Screen2() {
const {getEmail} = useContext(LoginContexts);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<LoginContexts.Consumer >
<Text>{getEmail}</Text>
</LoginContexts.Consumer>
</View>
);
}
export default Screen2;
This is my consumer part is which I want to display the text which is present in getEmail in
Text View. please help to solve this problem.
The issue is that LoginContexts.Consumer component expects a render function.
Context.Consumer
<MyContext.Consumer>
{value => /* render something based on the context value */}
</MyContext.Consumer>
The useContext hook is the consumer, remove the LoginContexts.Consumer component.
function Screen2() {
const {getEmail} = useContext(LoginContexts);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>{getEmail}</Text>
</View>
);
}

react-navigation from 1.x to 5, how to migrate redux actions

App has all old code which I am upgrading to latest versions. It was using Redux for state management with StackNavigator. Since that is not supported, I am not able to understand how to migrate my existing redux actions, which were changing screens on various events.
An example action:
export const goToHome = () => ({
type: PUSH,
routeName: 'projectList',
});
Which earlier reached navReducer, which handled POPing and PUSHing of screens.
export default (state = initialState, action) => {
let nextState;
switch (action.type) {
case NAV_POP:
nextState = AppNavigator.router.getStateForAction(
NavigationActions.goBack(),
state
);
break;
...
Please suggest.
Thanks.
The navigationRef is used for the scenarios like this.
You can refer the documentation here
It states
Sometimes you need to trigger a navigation action from places where
you do not have access to the navigation prop, such as a Redux
middleware. For such cases, you can dispatch navigation actions from
the navigation container
Which exactly is your requirement, here we create a navigationref and use call the navigation methods from there.
The below is the code for a simple example, you can use the 'navigate' inside your reducer. Also you will have to move it to a separate file just like they've provided in the documetation.
import * as React from 'react';
import { View, Button, Text } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
const navigationRef = React.createRef();
function navigate(name, params) {
navigationRef.current && navigationRef.current.navigate(name, params);
}
function Home() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title="Go to Settings"
onPress={() => navigate('Settings', { userName: 'Lucy' })}
/>
</View>
);
}
function Settings({ route }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Hello {route.params.userName}</Text>
<Button title="Go to Home" onPress={() => navigate('Home')} />
</View>
);
}
const RootStack = createStackNavigator();
export default function App() {
return (
<NavigationContainer ref={navigationRef}>
<RootStack.Navigator>
<RootStack.Screen name="Home" component={Home} />
<RootStack.Screen name="Settings" component={Settings} />
</RootStack.Navigator>
</NavigationContainer>
);
}
you can navigate through navigation container ref
you can call navigation().navigate("Settings") or navigation().goBack() in reducer
here is the demo of export navigation: https://snack.expo.io/#nomi9995/2eb7fd
App.js
import React,{useEffect} from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
const navRef = React.createRef();
export const navigation=()=>{
return navRef.current && navRef.current
}
const TestComponent=()=> {
useEffect(()=>{
setTimeout(() => {
navigation().navigate("Settings")
setTimeout(() => {
navigation().goBack()
}, 3000);
}, 100);
})
return (
<View style={styles.container}>
<Text>TestComponent 1</Text>
</View>
);
}
const TestComponent2=()=> {
return (
<View style={styles.container}>
<Text>TestComponent 2</Text>
</View>
);
}
const RootStack = createStackNavigator();
export default function App() {
return (
<NavigationContainer ref={navRef}>
<RootStack.Navigator>
<RootStack.Screen name="Home" component={TestComponent} />
<RootStack.Screen name="Settings" component={TestComponent2} />
</RootStack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
reducder.js
import { navigation } from 'path of App.js';
export default (state = initialState, action) => {
let nextState;
switch (action.type) {
case NAV_POP:
nextState = navigation().goBack()
break;

Conditional Rendering of child elements in React

I am trying to write a reusable Header Component in React-Native. I want to write it in a ways that the left and right button can be passed as child components. To know where to render which button I want to pass a prop like rightIcon or leftIcon. However I don't know how to access these props.
This is my App.js file
import React from 'react';
import {StyleSheet, TouchableHighlight, View} from 'react-native';
import Header from "./src/Header";
import {Ionicons} from '#expo/vector-icons';
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<Header headerText={"Barcode Scanner"}>
<TouchableHighlight righticon>
<Ionicons name="md-barcode" size={36} color="white"></Ionicons>
</TouchableHighlight>
</Header>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
},
});
And this is the Header Component
import React from 'react';
import {Text, View} from 'react-native';
export default class Header extends React.Component {
render() {
const {textStyle, viewStyle, rightButton} = styles;
return (
<View style={viewStyle}>
<Text style={textStyle}>{this.props.headerText}</Text>
<View style={rightButton}>
{this.renderRightChild()}
</View>
</View>
);
}
renderRightChild = () => {
console.log("Check if rightIcon Prop is set");
}
}
const styles = {
viewStyle: {
backgroundColor: '#5161b8',
justifyContent: 'center',
alignItems: 'center',
height: 80,
paddingTop: 25,
shadowColor: '#000',
shadowOffset: {width: 0, height: 2},
shadowOpacity: 0.2,
elevation: 2,
position: 'relative'
},
textStyle: {
color: '#fff',
fontSize: 20
},
rightButton: {
position: 'absolute',
top:
35,
right:
20
}
}
;
I already tried to use React.Children.toArray but this always throws an error that the request entity is too large.
Thanks for all the answers
I guess you can always use a render prop that way you can not only decide whether to render left/right icon component but the component rendering the icon does not even have to know what to render:
The term “render prop” refers to a simple technique for sharing code
between React components using a prop whose value is a function.
return (
<View style={styles.container}>
<Header
headerText={"Barcode Scanner"}
renderRightIcon={() => (
<TouchableHighlight righticon>
<Ionicons name="md-barcode" size={36} color="white" />
</TouchableHighlight>
)}
/>
</View>
);
Then you can use call the right icon as a function:
return (
<View style={viewStyle}>
<Text style={textStyle}>{this.props.headerText}</Text>
{renderLeftIcon && (
<View style={leftButton}>
{renderLeftIcon()}
</View>)
}
{renderRightIcon && (
<View style={rightButton}>
{renderRightIcon()}
</View>)
}
</View>
);
You render both components, the right and left and you put an if condition inside state.
Header Component render method
render() {
const { leftOrRight } = this.props // right - true, left - false
return(
...
{ leftOrRight ? <RightIcon /> : <LeftIcon />}
);
}
Inside Component that calls Header
import Header from './somepath ...';
class Something extends React.Component {
this.state = { leftOrRight }
render() {
return(
<Header leftOrRight = {this.state.LeftOrRight}/>
);
}
}
You could have a function that sets leftOrRight in your parent class
One way to do this is write a Header Component and pass all the things, as props, which you can then access them in Header Components Props like..
<Header title="HeaderTitle"
leftButtonTitle="LeftButton"
rightButton={canBeAObjectWithSomeInfo}
leftButtonClick={handleClick} />
and then in your header component(can be class or a function)
const Header = ({}) => (
<View>
<View onPress={this.props.handleClick}>{this.props.leftButton}</View>
<View>{this.props.title}</View>
<View onPress={this.props.handleRightClick}>{this.props.rightButton}</View>
</View>
)
something like this you can have and then you can design header accordingly

React Native : 'undefined is not a function' while using navigator

I am trying to make an app with 2 panels, one with my user inputs, and the other one with the output (it's all dates, not important).
I am using navigator. My 'main' class is :
export default class App extends Component {
onDateChange = (state) => (event,value) => {
this.setState({
[state]:value
});
}
render () {
return (
<Navigator
initialRoute={{id: 'mainPage', name: 'main'}}
renderScene={this.renderScene.bind(this)}
configureScene={(route) => {
if (route.sceneConfig) {
return route.sceneConfig
}
return Navigator.SceneConfigs.VerticalDownSwipeJump;
}}/>
);
}
renderScene ( route, navigator ) {
var routeId = route.id;
if (routeId === 'mainPage') {
return (
<mainPage
navigator={navigator}/>
);
}
if (routeId === 'calcPage') {
return (
<calcPage
navigator={navigator}/>
);
}
}
}
And the first page it loads looks like :
class mainPage extends Component {
componentWillMount () {
var navigator = this.props.navigator;
}
constructor(props) {
super(props)
this.state = {
dayMes: "",
monthMes: "",
yearMes: "",
dayLast: "",
monthLast: "",
yearLast: "",
dayInit: "",
monthInit: "",
yearInit: "",
}
}
render() {
return (
<ScrollView>
<View style={{
justifyContent: 'center',
}}>
<View style={{
height: 40,
justifyContent: 'center',
borderTopWidth: 25,
backgroundColor: 'white'
}}/>
</View>
LOTS of textInputs ...
<TouchableOpacity onPress={ () => { navigator.replace({ id: 'calcPage' }); } }>
<View style={styles.button}>
<Text style={styles.buttonText}>Calculer</Text>
</View>
</TouchableOpacity>
</View>
</ScrollView>
)
}
}
When i press the button though, it gives me an error undefined is not a function (evaluating 'navigator.replace({id: 'calcPage'})'). I guess the problem is with the function it triggers, but i don't know where. The calcPage class is the same as the first one, since i'm still testing.
Thanks for the help !
EDIT :
import React, {Component} from 'react';
import DatePicker from 'react-native-datepicker';
import { AppRegistry, View, Image, Text, TextInput, StyleSheet, Alert, Button, TouchableOpacity, ScrollView, Navigator } from 'react-native';
my imports

How to navigate page with React Native

I have a component for listing items, I want to add the function that can go to a different page, and that page has the detail about that item. Currently, this is my code for listing items.
import React, { Component } from 'react';
import { ScrollView } from 'react-native';
import axios from 'axios';
import CarDetail from './CarDetail';
const API_URL = 'http://localhost:3000';
class CarList extends Component {
state = { cars: [] };
componentWillMount() {
console.log('Mount');
axios.get(`${API_URL}/cars`)
.then(response => this.setState({ cars: response.data.cars }));
}
renderCars() {
return this.state.cars.map(car => <CarDetail key={car.id} car={car} />
);
}
render() {
console.log(this.state.cars);
return (
<ScrollView>
{this.renderCars()}
</ScrollView>
);
}
}
export default CarList;
and this is the code for describing items
import React from 'react';
import { Text, View, Image } from 'react-native';
import { Actions } from 'react-native-router-flux';
import Card from '../material/Card';
import CardSection from '../material/CardSection';
const CarDetail = ({ car }) => {
const imageURI = 'https://yt3.ggpht.com/-HwO-2lhD4Co/AAAAAAAAAAI/AAAAAAAAAAA/p9WjzQD2-hU/s900-c-k-no-mo-rj-c0xffffff/photo.jpg';
const { make, model } = car;
function showCarDetail() {
Actions.showCar();
}
return (
<Card>
<CardSection>
<View style={styles.containerStyle}>
<Image
style={styles.imageStyle}
source={{ uri: imageURI }}
/>
</View>
<View style={styles.headContentStyle}>
<Text
style={styles.headerTextStyle}
onPress={showCarDetail()}
>
{make}
</Text>
<Text>{model}</Text>
</View>
</CardSection>
<CardSection>
<Image
style={styles.picStyle}
source={require('./car.jpg')}
/>
</CardSection>
</Card>
);
};
const styles = {
headContentStyle: {
flexDirection: 'column',
justifyContent: 'space-around'
},
headerTextStyle: {
fontSize: 18
},
imageStyle: {
height: 50,
width: 50
},
containerStyle: {
justifyContent: 'center',
alignItems: 'center',
marginLeft: 10,
marginRight: 10
},
picStyle: {
height: 300,
flex: 1,
width: null
}
};
export default CarDetail;
How can I change my code for that? Can anyone give me an example?
You have to use some sort of navigation component. There are many out there, but personally I use the one that is built into React Native. https://facebook.github.io/react-native/docs/navigator.html

Categories

Resources