I have a React Native app working using NavigatorIOS API, and am trying to translate the same code to use Navigator API. My trouble is when creating a NavigatorIOS component, the navigator is implicitly passed, whereas in Navigator, you must call renderScene and make your own navigator prop. I'm not sure what navigator to pass (do i create one or what?).
The structure of the app is that there are 2 tabs: Book and Search, and BookList is another component which lists all the book entries, so that when you press one of them, it brings you to BookDetail. Right now, pressing a book brings me to the same page (BookList). I think this is because there's only one page in the route, but I'm not sure how to initialize the route and navigator.
This is where I call the navigator to push a scene onto the route in BookList:
showBookDetail(book) {
this.props.navigator.push({
title: book.volumeInfo.title,
component: BookDetail,
passProps: {book}
});
}
This is the NavigatorIOS version:
class Books extends React.Component {
render() {
return (
<NavigatorIOS
style={styles.container}
initialRoute={{
title: 'Featured Books',
component: BookList
}}/>
);
}
This is my Navigator version that is not working:
class Books extends React.Component {
render() {
return (
<Navigator
style={styles.container}
initialRoute={{ title: 'Featured Books', index: 0}}
renderScene={(route, navigator) =>
<BookList title={route.title} navigator={navigator}/>
}
/>
);
}
I see the body of your renderScene function returns the <BookList> component, so it's not really surprising that when it is called you just see another BookList. Try something like this instead:
renderScene={(route, navigator) =>
<route.component title={route.title} navigator={navigator}/>
}
As long as you have imported or required BookDetail in the source file for Books, renderScene will be passed the route object you specified in navigator.push, which has the component property BookDetail
Note that you will also have to change your initialRoute to have a component property set to BookList if you take this approach
Related
I upgraded native-base library from 2.13.14 to 3.0.3 and wrapped my App.js content in NativeBaseProvider. Here is the error I got after doing this:
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
--
My Code:
import React, {useEffect} from 'react';
import {I18nManager, ActivityIndicator} from 'react-native';
import {NavigationContainer} from '#react-navigation/native';
import {NativeBaseProvider, Container} from 'native-base';
import TrackPlayer from 'react-native-track-player';
import {PlayerContextProvider} from './contexts/PlayerContext';
import MainStackNavigator from './Navigators/MainStackNavigator';
const App = () => {
const [isReady, setIsReady] = React.useState(false);
useEffect(() => {
TrackPlayer.setupPlayer().then(() => {
console.log('player is setup');
TrackPlayer.updateOptions({
capabilities: [
TrackPlayer.CAPABILITY_PLAY,
TrackPlayer.CAPABILITY_PAUSE,
TrackPlayer.CAPABILITY_STOP,
TrackPlayer.CAPABILITY_JUMP_BACKWARD,
TrackPlayer.CAPABILITY_JUMP_FORWARD,
],
jumpInterval: 30,
});
setIsReady(true);
});
}, []);
return (
<NativeBaseProvider>
<Container>
{isReady ? (
<PlayerContextProvider>
<NavigationContainer>
<MainStackNavigator />
</NavigationContainer>
</PlayerContextProvider>
) : (
<Container>
<ActivityIndicator />
</Container>
)}
</Container>
</NativeBaseProvider>
);
};
I18nManager.forceRTL(true);
export default App;
I did found the root cause of this problem (in my case). The approach was to go down the navigator structure (in your case start with MainStackNavigator) and replace the rendered component with the example code from https://docs.nativebase.io/setup-provider until it breaks:
function Test() {
return (
<Box flex={1} bg="#fff" alignItems="center" justifyContent="center">
<Text>Open up App.js to start working on your app!</Text>
</Box>
)
}
In my case it lead me to a component which was using deprecated native-base elements from v2. Sadly there is no good deprecation guide or migration guide which tells you how to upgrade from v2 to v3.
So following this approach might lead out of this situation (the error message is not really helpful). Keep an eye on the following deprecated components or functions and replace them as follows:
Header -> use HStack docs.nativebase.io/3.0.0/migration/Header
Body -> use Box or Container (not documented)
Left -> use Center or remove (not documented)
Right -> use Center or remove (not documented)
getTheme -> use extendTheme docs.nativebase.io/setup-provider#add-custom-theme-optional
StyleProvider -> use NativeBaseProvider docs.nativebase.io/setup-provider
This is my list of deprecated components so far. In addition here are some update guides for
Icon docs.nativebase.io/3.0.0/migration/Icon
Button docs.nativebase.io/3.0.0/migration/Button
and there are a lot more to be found on this index page https://docs.nativebase.io/3.0.0/migration/
I hope It helps you solving this problem.
Have a try to move the forceRTL method inside the useEffect of the app initialization.
like :
useEffect(() => {
I18nManager.forceRTL(true); // here..
TrackPlayer.setupPlayer().then(() => {
Might it will help you.
When I am using Navigator from react native I am getting an error. The error is:
Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check your code at App.js:11.
My code is:
import React, { Component } from 'react';
import {
Text,
Navigator,
TouchableHighlight
} from 'react-native';
export default class App extends Component {
render() {
return (
<Navigator
initialRoute={{ title: 'Awesome Scene', index: 0 }}
renderScene={(route, navigator) => (
<Text>Hello {route.title}!</Text>
)}
style={{ padding: 100 }}
/>
);
}
}
I followed this tutorial: https://reactnative.dev/docs/0.43/navigator
Can someone please help me in this. Is there something wrong in documentation?
This code example is from react doc version 0.43. After that "Navigator" was removed.
If you do not care about backward compatibility then I would suggest following current documentation (0.63). In the latest version, there is more simple solution is given.
I'm trying to make a SignUp/Login screen with a stackNavigator.
But when I try to navigate from the login page to the signup page.
I've tried creating a const that would be the navigate property but I get the same. If I do all my screens in the same file with the stackNavigator I don't get error but this is not really what I want to do.
Here is my Login Screen first (before getting to this screen in index.js I call the Welcome class to check if the user is logged in are not and I return the if he's not
export default class Login extends React.Component{
render(){
return(
<ScrollView style={{padding : 20}}>
<Text style={styles.title}>
Login
</Text>
<TextInput style={styles.input} placeholder="Email Address"/>
<TextInput style={styles.input} placeholder="Password" />
<View style={{margin : 7}}/>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('Signup')}
>
<Text> Don't have an account? Click here</Text>
</TouchableOpacity>
<Button
onPress={this.props.onLoginPress}
title="Log in"
color='grey'
/>
</ScrollView>
);
}
}
My sign up screen is pretty empty it only returns a View with a title as of right now
And finally here's my StackNavigator file
const AppNavigator = createStackNavigator({
Welcome: {
screen : Welcome
} ,
Login: {
screen: Login
},
Signup : {
screen : Signup
},
},
{
initialRouteName: "Welcome"
});
export default createAppContainer(AppNavigator);
Where Welcome is the file that checked if user is logged in are not
The error I get is "error undefined is not an object (evaluating '_this.props.navigation.navigate')." When I click the in Login.js
checked your repo. Your welcome screen first checks if the user is logged in or not and if not logged in, it renders the login screen. You're not navigating from Welcome screen to Login screen, but rendering another Login screen. The problem lies here. I'll give an example:
In your phone, say iPhone X, you downloaded and installed Facebook from the App Store. Then you logged into your account. That means, the Facebook app in your phone is now configured to use your account. Now, if you want to post your photo, you wont be able to do it if you download and install another Facebook app and try to post from there. You will have to use the same old app which is configured to use your account. (Actually you won't be able to download two at a time :P)
In the same way, you should tell the stack navigator to navigate to the stack navigator's login screen, not render another login screen. The new login screen you created won't have the navigation prop, unless you pass one.
But there is another way. You can render the signup screen the same way you render your login screen. Copy this code to your Welcome.js:
import React from 'react';
import { Text, View, Button } from 'react-native';
import LoginScreen from './LoginScreen';
import SignupScreen from './Signup';
import DrawerNavigator from '../navigation/DrawerNavigator';
const notLoggedIn=0, loggedIn=1, signingUp=2;
export default class Welcome extends React.Component{
constructor(props){
super(props);
this.state={
state: notLoggedIn
}
}
render(){
switch (this.state.state){
case notLoggedIn:
return (
<LoginScreen
onLoginPress={()=>this.setState({state:loggedIn})}
onSignUpPress={()=>this.setState({state:signingUp})}
/>
);
case loggedIn:
return <DrawerNavigator />;
case signingUp:
return <SignupScreen />; //Add onBackPress={()=>this.setState({state:notLoggedIn})}} prop here if you want
}
}
}
Then in your Login.js, change onPress prop from this.props.navigation.navigate('Signup') to this.props.onSignUpPress().
You won't need the stack navigator now.(You've your own navigator now) So, instead of using that stack navigator, you can now directly use your Welcome screen. Now you can just delete the stacknavigator.js file.
This should work out, I guess.
Tip: Saw that you have many unused styles and imports in your files. If you're not really going to use them, you can just remove those lines. That's up to you.
EDIT:
To pass a function into a navigator, you can use the screenProps prop.
Edit your Welcome.js:
case loggedIn:
return <DrawerNavigator logout={()=>this.setState({state:notLoggedIn})} />;
DrawerNavigator.js:
...
const DrawerNavigator = createAppContainer(createDrawerNavigator(
...
));
export default class navigator extends React.Component {
render(){
return <DrawerNavigator screenProps={{logout: this.props.logout}} />
}
}
And inside your logout.js, call the function this.props.screenProps.logout() from wherever you want.
This should work.
I'm using Flatlist in HomeScreen and it shows me multiple posts. So now what I want is whenever I Signout from the app or close the app and then open the app, I should be able to see the first item from Flatlist.
I've tried using scrollToIndex inside my render function but it gave me an error - undefined is not an object (evaluating '_this2.refs.flatListRef.scrollToIndex')
<FlatList
data={this.state.data}
ref={(ref) => { this.flatListRef = ref; }}
renderItem={ ({item,index}) => this._renderItem(item,index) }
extraData={[ this.state.data, this.state.checked ]}
/>
And this is what I tried using in componentDidMount and inside render function this.refs.flatListRef.scrollToIndex({animated: true,index:0}); but didn't work.
ComponentDidMount callbacks won't run after the user leaves the app and resumes it later. You have to use AppState instead:
AppState can tell you if the app is in the foreground or background, and notify you when the state changes. [Source]
Adapt the given example to your needs and scroll with this.flatListRef.scrollToIndex({animated: true,index:0})}
import {
ScrollView,
} from 'react-native';
import {useScrollToTop} from '#react-navigation/native';
const ref = React.useRef(null);
useScrollToTop(ref);
const goToTopClickAction = () => {
ref.current?.scrollTo({
y: 0,
animated: true,
});
};
add ref={ref} in ScrollView tag
< ScrollView ref={ref}> < /ScrollView >
I'm trying to create an Android version of my React Native app but I'm having problems getting my head around the Android navigator. The code I use in my index.ios.js is:
render() {
return (
<React.NavigatorIOS
style={styles.container}
initialRoute={{
title: 'Product Searcher',
component: ProductPage,
}}/>
);}
How do I create an equivalent Navigator for Android in my index.android.js? I've looked at this piece of documentation (https://facebook.github.io/react-native/docs/navigator.html) but I can't seem to find a way to produce equivalent navigator logic using this tutorial even though my iOS navigator is very simple.
A very simple use of the Navigator would be something like:
class MyApp extends Component {
render() {
return (
<Navigator
initialRoute={name: 'productpage'}
renderScene={this.renderScene}
/>)
}
renderScene(route, navigator) {
switch (route.name) {
case 'productpage':
return (<ProductPage navigator={navigator}/>)
}};
In renderScene you will define all your scenes for the app. By passing the navigator as a property to the different scenes you can access the navigator.