The component for route '...' must be a React component - react-native

I have a React component (Highlight.js) that accepts two properties, image and description. The user will swipe through 4 different screens with it's own background image and description, as an introduction to the app.
TestScreen.js is where I've setup the logic for navigating between screens. When i try to set the screen in my AppContainer as the Highlight component, I keep getting the error that 'The component for route 'Page1' must be a React component.
I've tried to play around a lot with the code to see how I can manage to get it working and the only way it does is when I don't mention the image and description properties, in which case neither is displayed and the screen is mostly blank.
Highlight.js
import React, { Component } from 'react';
import { View, Text, Image, ImageBackground } from 'react-native';
import { NextButton } from '../Buttons';
import styles from './styles';
export default class Highlight extends Component {
render() {
return (
<ImageBackground style={styles.container}>
<Image
style={styles.image}
source={this.props.image} // the image goes here
resizeMode="cover"
/>
<View style={styles.textContainer}>
<Text style={styles.text1}>MYAPP</Text>
<Text style={styles.text2}>Highlights</Text>
<Text style={styles.text3}>{this.props.description}</Text> // the description goes here
</View>
<View style={styles.buttonContainer}>
<NextButton />
</View>
</ImageBackground>
);
}
}
TestScreen.js
import React, { Component } from 'react';
import { createAppContainer, createStackNavigator } from 'react-navigation';
import { Highlight } from '../components/Highlights';
export default class TestScreen extends Component {
render() {
return <AppContainer />;
}
}
const AppContainer = createAppContainer(
createStackNavigator({
Page1: {
screen: (
<Highlight
image={require('../components/Highlights/images/highlight1.png')}
description={
'Avoid No-Show of Candidates after setting up an interview'
}
/>
)
}
})
);
I expect the screen to display content with the image and description properties. Currently I have 4 separate components with essentially the same code (except for the image and description). How to I tackle this problem and avoid the repetition of code?

A brief explanation
you're passing an element to the route not a Component itself
() => (<Highlight ... />)
this creates a new function Component which returns your expected element,
that's why it worked

Related

Error: Couldn't find a navigation object. Is your component inside NavigationContainer?

I am trying to navigate from my login screen to the home screen using a pressable button. However, the code that I now have gives the following error: Couldn't find a navigation object. Is your component inside NavigationContainer?. The error is aimed on row 8 (const navigation = useNavigation();).
LoginButton.js:
import React, { Component } from "react";
import { View, Text, Image, Pressable, Button } from "react-native";
import { useNavigation } from "#react-navigation/native";
import styles from "./styles";
import HomeScreen from "../../screens/Home";
const LoginButton = () => {
const navigation = useNavigation();
return (
<View style={styles.container}>
<Pressable
onPress={() => navigation.navigate("Home")}
style={styles.button}
>
<Text style={styles.buttontext}>Login</Text>
</Pressable>
</View>
);
};
export default LoginButton;
The LoginButton is inserted as a component in the LoginItem inside the last <View. LoginItem.js:
import React from "react";
import { View, Text, Image } from "react-native";
import styles from "./styles";
import LoginButton from "../LoginButton";
import { createNativeStackNavigator } from "#react-navigation/native-stack";
const Stack = createNativeStackNavigator();
const LoginItem = (props) => {
return (
<View style={styles.logincontainer}>
{/* Background image of the login screen */}
<Image
source={require("../../../assets/images/blue-sky-start-screen.jpg")}
style={styles.loginbackground}
blurRadius={4}
></Image>
{/* Title of the login screen */}
<View style={styles.titlecontainer}>
<Text style={styles.companytitle}>BestelSnel</Text>
</View>
{/* Login button */}
<View style={styles.loginbutton}>
<LoginButton></LoginButton>
</View>
</View>
);
};
export default LoginItem;
useNavigation only works if your component is inside of a NavigationContainer and a Navigator like mentioned on the getting-started-page:
https://reactnavigation.org/docs/hello-react-navigation
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>
HomeScreen would be your LoginItem
In my case, I got this error because autocomplete added the wrong import statement.
import { useNavigation } from "#react-navigation/core";
but the one I needed is:
import { useNavigation } from "#react-navigation/native";
Hope this helps anybody to save some minutes.
I had this issue as well, and the accepted answer did not work for me.
What I did get working on was importing the component I wanted to go to and using that in the navigate() call. Generally, this probably means just removing the quotes so your param to that function is a Component and not a String.
Given the code from the question, the code would be:
onPress={() => navigation.navigate(Home)}
Very similar to the original, just minus the quotes, as mentioned.
I was passing the params correctly also, but params had a react component in it. That's why it was giving this error.
// product should not have a react component
props.navigation.navigate(ROUTE_TEST, {product});

How to navigation goback function works in React native page including Webview

I am working with Webview in React native.
Header and footer is not webview, and header includes navigation.goBack() button in it.
But it is not working even footer has navigation.navigate() button which is working.
Here is my code.
import 'react-native-gesture-handler';
import * as React from "react";
import { ImageBackground, StyleSheet, Text, View, Image, TextInput, TouchableOpacity } from "react-native";
import { AntDesign } from '#expo/vector-icons';
import { WebView } from 'react-native-webview';
import { height, width } from 'react-native-dimension';
import HeaderLink from './header_link';
import Footer from './footer';
const styles = StyleSheet.create({
container: {
flex: 1,
// flexDirection: "column"
},
});
class LinkPageScreen extends React.Component {
render() {
this.state = {
url: this.props.route.params.url,
title: this.props.route.params.title,
};
var linkURL = this.state.url;
return (
<View style={styles.container}>
<HeaderLink
image={false}
imageSource={{}}
left={
<TouchableOpacity onPress={ () => this.props.navigation.goBack(null) }>
....
</TouchableOpacity>
}
/>
<WebView
source={{uri: linkURL}}
style={{marginTop: height(12), marginBottom: height(11)}}
/>
<Footer navigation={this.props.navigation} />
</View>
)
}
}
export default LinkPageScreen
I changed navigation.goBack into navigation.navigate() but it was not working also in header.
I checked for same error on stackoverflow but webview.goback() function was there, and my problem is not like that. I am sure.
I found the solution. Even I don't know why.
I changed the code like below.
class LinkPageScreen extend React.component {
render() {
return (
<View>
<WebView .... />
<Header />
<Footer />
......
Unbelivably it works, i just changed the order of tags between webview and header.
Anyone can explain to me why this is happening?
Thanks.
You can try the code below:
import { useNavigation } from '#react-navigation/native';
const {goBack} = useNavigation();
const navigateBack = useCallback(() => {
goBack();
}, [goBack]);
.
.
.
<TouchableOpacity onPress={navigateBack}>
....
</TouchableOpacity>

Navigation.getParam is undefined while trying to pass function as parameter

I'm trying to use a function from my Main component in my details component which I user react navigation to navigate to and I want to save some changes in detail screen in my main component
//Main.js
import React from 'react';
import {
StyleSheet ,
Text,
View,
TextInput,
ScrollView,
TouchableOpacity,
KeyboardAvoidingView,
AsyncStorage
} from 'react-native'
import Note from './Note'
import { createStackNavigator, createAppContainer } from "react-navigation";
import Details from './Details';
export default class Main extends React.Component {
static navigationOptions = {
title: 'To do list',
headerStyle: {
backgroundColor: '#f4511e',
},
};
constructor(props){
super(props);
this.state = {
noteArray: [],
noteText: ''
};
}
render() {
let notes = this.state.noteArray.map((val,key) => {
return <Note key={key} keyval={key} val={val}
goToDetailPage= {() => this.goToNoteDetail(key)}
/>
});
const { navigation } = this.props;
return(
<View style={styles.container}>
<ScrollView style={styles.scrollContainer}>
{notes}
</ScrollView>
<Details saveEdit={this.saveEdit} />
</View>
);
}
goToNoteDetail=(key)=>{
this.props.navigation.navigate('DetailsScreen', {
selectedTask: this.state.noteArray[key],
saveEdit: this.saveEdit
});
}
saveEdit = (editedTask,dueDate) => {
this.state.noteArray.push({
'creationDate': editedTask['creationDate'],
'taskName': editedTask['taskName'],
'dueDate': dueDate
});
this.setState({noteArray:this.state.noteArray})
this.saveUserTasks(this.state.noteArray)
}
this.setState({noteArray:this.state.noteArray})
this.saveUserTasks(this.state.noteArray)
}
}
Then I try to use it as prop in my Detail.js
import React from 'react';
import {
StyleSheet ,
Text,
View,
TextInput,
Button,
TouchableOpacity,
} from 'react-native'
import { createStackNavigator, createAppContainer } from "react-navigation";
export default class Details extends React.Component {
constructor(props){
super(props);
this.state = {
dueDate = ''
}
}
static navigationOptions = {
headerStyle: {
backgroundColor: '#f4511e',
},
};
componentDidMount = () => {
this.getUserTasks()
}
render() {
const { navigation } = this.props;
const selectedTask = navigation.getParam('selectedTask', 'task');
var { saveEdit} = this.props;
return(
<View key={this.props.keyval} style={styles.container}>
<View style = { styles.info}>
<Text style= {styles.labelStyle}> Due date:
</Text>
<TextInput
onChangeText={(dueData) => this.setState({dueData})}
style={styles.textInput}
placeholder= {selectedTask['dueDate']}
placeholderTextColor='gray'
underlineColorAndroid = 'transparent'
>
</TextInput>
</View>
<TouchableOpacity onPress={this.props.saveEdit(selectedTask, this.state.dueDate)} style={styles.saveButton}>
<Text style={styles.saveButtonText}> save </Text>
</TouchableOpacity>
</View>
);
}
}
I searched a lot to find the solution and I tried many of them but get different undefined errors. This is not what I did in the first place but when I search I found this solution here. And I know it causes lots of issues.
I want to know how can I manage to access to main method from details and pass parameters to it or how can I manage to use main props in my details component
If you are using react-navigation 5, params is no longer under the navigation object but under route object. This is the link to the sample code:
https://reactnavigation.org/docs/params
Solution
<Details saveEdit={this.saveEdit} />
to
<Details navigation={this.props.navigation} saveEdit={this.saveEdit} />
render() {
return(
<View style={styles.container}>
<ScrollView style={styles.scrollContainer}>
{notes}
</ScrollView>
<Details navigation={this.props.navigation} saveEdit={this.saveEdit} />
</View>
);
}
Why?
You are using your Details component in Main screen. So you need to give navigation to Details's props from your Main to use navigation props in Details component.
Because your Details component is not the screen component registered in your navigator(router).
I tried to run your code on my machine but it seems you have too many syntax error in your code (maybe because of copy pasta?)
but it seems you should change
<TouchableOpacity onPress={this.props.saveEdit(selectedTask, this.state.dueDate)}
in Detals.js to
<TouchableOpacity onPress={this.props.navigation.getParams('saveEdit')(selectedTask, this.state.dueDate)}
for clarification this worked for me
in MainPage.js
_test(){
console.log('test');
}
.
.
.
<ActionButton
buttonColor="rgba(231,76,60,1)"
onPress={() => NavigationService.navigate('AddNewSession', {test: this._test})}>
</ActionButton>
and in AddNewSession.js
componentDidMount()
let test = this.props.navigation.getParam('test');
test();
}
There are many mistakes within your codes. First of all you are importing the navigation build-in function {createStackNavigator} in all your files, Main.js and Details.js :
import { createStackNavigator, createAppContainer } from
"react-navigation";
That make me think that you didn't know how the stack navigation or navigation in general functions in react native. You should have a file that handles your routes configuration, let call it MyNavigation.js and then define the routes 'Main' and 'details' in MyNavigations,js. It's only inside MyNavigation.js that you can import "createStackNavigator". Then you will define your functions to move between the screens "Main" and "detail". Those functions will be passed as props to the routes when moving between one another. The overall action wihtin MyNavigation.js will look like:
import React from 'react';
import { createStackNavigator } from '#react-navigation/stack';
import { NavigationContainer } from '#react-navigation/native';
import Main from './Main';
import Detail from './Detail';
const Stack = createStackNavigator();
function goToDetailFromMainScreen(){
return(this.props.navigation.navigate('switch2'));
}
function DetailSaves(){
return(//your code here to save details);
}
//Here you pass the functions to Main Component to handele Detail componets
's actions
function switch1(){
return(<Main GoToDetails={() => this.goTodetailFromMainScreen()} paramsForDetailActions={() => this.detailSaves()} />)
}
function switch2(){
return(<Details />)
}
export default function MyNavigation() {
return(
<NavigationContainer>
<Stack.Navigator initialRouteName='switch1'>
<Stack.Screen name='switch1' options={{header:()=>null}} component={Main} />
<Stack.Screen name='switch2' options={{headerTitle:"Detail"}} component={Detail} />
</Stack.Navigator>
</NavigationContainer>
)
}
Now inside Main.js you check the props functions passed to it from MyNavigation.js:
// Main.js
constructor(props){
super(props);
}
goToDetails = () => {
this.props.onPress?.();
}
paramsForDetailActions= () => {
this.props.onPress?.();
}

Navigate from one stack to another

I have a nested navigation structure.
A Sign-In screen that needs to navigate to Profile screen and Profile needs to navigate to Inbox. Like so, Sign In -> Profile -> Inbox.
Sing In
import React, {Component} from 'react';
import Profile from "../Profile";
class AppSignIn extends React.Component{
_doSignIn(){
this.props.navigation.navigate('Profile')
}
render(){
return(
<View>
<TouchableHighlight onPress={this._doSignIn.bind(this)}>
<Text>Go to Profile</Text>
</TouchableHighlight>
</View>
)
}
}
const AppSignInNav = createStackNavigator(
{
AppSignIn:{
screen : AppSignIn
},
Profile:{
screen:Profile
}
});
export default createAppContainer(AppSignInNav);
Profile
import React, {Component} from 'react';
import Inbox from "../Inbox";
class Profile extends React.Component{
_goToInbox(){
this.props.navigation.navigate('Inbox')
}
render(){
return(
<View>
<TouchableHighlight onPress={this._goToInbox.bind(this)}>
<Text>Go to Inbox</Text>
</TouchableHighlight>
</View>
)
}
}
const ProfileNav = createStackNavigator(
{
Profile:{
screen : Profile
},
Inbox:{
screen : Inbox
}
});
export default createAppContainer(ProfileNav);
How ever i get this error as a result of trying to navigate from createStackNavigator to another.
The component for route 'Profile' must be a React component...
How can i export Profile as a React Component but still have it navigate to Inbox.?
There can be only one appContainer for navigation. You are trying to create the app container twice using createAppContainer.
You can use AppSignInNav as the app container and export the ProfileNav as a normal StackNavigator.
Refer this link for details on createAppContainer and its use.

React native got blank screen

I'm new to react-native, and I'm trying to use a navigator to switch between different scenes. However, when the simulator runs, instead of printing an error, I just got an empty, blank, white screen that shows nothing expect the remaining battery, the time, and the wifi signal. I checked my code many times and cannot find an error. Can someone help me on this?
This is my index.ios.js file:
import React, { Component } from 'react';
import {
AppRegistry,
Text,
View,
Navigator
} from 'react-native';
import Chatroom from './Views/Chatroom';
import Chat from './Views/Chat';
class goals extends Component{
render(){
return(
<Navigator
initialRoute={{screen: 'Chatroom'}}
renderScene={(route, nav) => {return this.renderScene(route.screen)}}
/>
)
}
renderScene(route,nav) {
switch (route.screen) {
case 'Chatroom':
return <Chatroom navigator={nav} />
case 'Chat':
return <Chat navigator={nav} />
}
}
}
AppRegistry.registerComponent('goals', () => goals);
This is my Chat.js file:
import React, { Component } from 'react';
import { View, Text, TouchableHighlight } from 'react-native';
export default class Chat extends Component {
render() {
return (
<View>
<Text>This is chat</Text>
<TouchableHighlight onPress={this.gochatroom.bind(this)}>
<Text>Go to chatroom</Text>
</TouchableHighlight>
</View>
)
}
gochatroom() {
this.props.navigator.push({ screen: 'Chatroom' });
}
}
This is my Chatroom.js file:
import React, { Component } from 'react';
import { View, Text, TouchableHighlight } from 'react-native';
export default class Chatroom extends Component {
render() {
return (
<View>
<Text>This is chatroom</Text>
<TouchableHighlight onPress={this.gochat.bind(this)}>
<Text>Go to chat</Text>
</TouchableHighlight>
</View>
)
}
gochat() {
this.props.navigator.push({ screen: 'Chat' });
}
}
You aren't passing the right arguments to renderScene. The following should work better:
renderScene={(route, nav) => this.renderScene(route, nav)}