I have made an splash page looks like this:
export default class Splash extends Component {
performTimeConsumingTask = async () => {
return new Promise((resolve) =>
setTimeout(() => {
resolve('result');
}),
);
};
async componentDidMount() {
const data = await this.performTimeConsumingTask();
// const navigation = useNavigation();
if (data !== null) {
this.props.navigation.navigate('BottomMainNavgigation');
}
}
render() {
return (
<View style={styles.viewStyles}>
{/* <Text style={styles.textStyles}>Welcome</Text> */}
<Image
style={styles.tinyLogo}
source={{
uri: URL.logov2,
}}
/>
</View>
);
}
}
THen I use this like this, in my navigation:
const RootStackScreen = (props) => {
const [t] = useTranslation();
return (
<SplashRootStack.Navigator>
<SplashRootStack.Screen
name="Main"
component={Splash}
options={{headerShown: false, headerBackTitle: t('back')}}
/>
<SplashRootStack.Screen
name="BottomMainNavgigation"
component={BottomMainNavgigation}
options={{headerShown: false, headerBackTitle: t('back')}}
/>
</SplashRootStack.Navigator>
);
};
and also:
<PaperProvider theme={MyTheme}>
<NavigationContainer linking={linking} fallback={<Splash />}>
<RootStackScreen />
</NavigationContainer>
</PaperProvider>
and like this in my app.js:
const App = () => {
return (
<Provider store={store}>
<PersistGate loading={<Splash />} persistor={persistor}>
<Suspense fallback={<Splash />}>
<Navigation />
</Suspense>
</PersistGate>
</Provider>
);
};
export default App;
when I run the application it looks like this:
there is a warning :
I get this warning with id= 0, 1 and 2 and I get this warning also:
what have I donr incorrectly ad how can I remove these warnings, also when I load the app in the emulator, I get a white screen for few seconds before I get my own splash page and then to my app.
how can I do this better?
You are using Splash component in redux persist as a loader and in your Splash component there isn't any navigation prop available because it's a parent component and not the part of navigation tree you need to use switch navigator for the same purpose but with the current structure navigation will not work unless you move navigation part inside the navigator tree. Now the solution is,
Use only splash as a static UI component.
Move you navigation or componentDidMount logic inside the stack navigator.
Add simple Activity indicator as fallback.
<PersistGate
loading={<ActivityIndicator style={{top: '45%'}}
animating color={theme.appColor} size='large' />}
persistor={ReduxStore.persistor}>
<Navigator />
</PersistGate>
Your warnings
Undefined is not an object :
The problem is that you are using the Splash as the fallback component so until your deeplink is resolved Splash would be displayed and the Splash here is not part of navigation which will not get the 'navigation' prop so you are getting the warning.
Same for the other higher order components like PersistGate and suspense you can given the splash for everything and all this is outsider navigation.
resolution : Use the activity indicator instead of splash for the fallback
This is due to one of your middleware in redux taking longer, better check your redux middleware.
White screen,
this is whats causing the white screen maybe caused by the same reason as your middleware warning or the component did mount of the splash screen. And you have several providers so better remove one or two and check whats causing that.
You can check this sample to get an idea on using splash screens and authentication.
https://snack.expo.io/#guruparan/rnn-v5
Related
For some reason I can see the elements when run on web (using Expo) and they render perfectly when I click "Run in web browser".
However when I try to do it on iOS or Android, it just shows a blank screen and only shows the LinearGradient.
When I run the Android Emulator and combine it with the React Native Dev tool, I can see the elements in the devtool but they dont show up in the emulator. The component I try to render is called Login.tsx and If i copy all the content in Login.tsx to my App.tsx it renders perfectly on Web, Android and iOS.
What am I doing wrong?
const Stack = createNativeStackNavigator();
const Navigation: React.FC = () => {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Login"
>
<Stack.Screen
name="Login"
component={Login}
/>
<Stack.Screen
name="LostPassword"
component={LostPassword}
/>
<Stack.Screen
name="Register"
component={Register}
/>
</Stack.Navigator>
</NavigationContainer>)
}
export default Navigation;
const App: React.FC = () => {
return (
<LinearGradient
colors={["#2D1C96", '#190E60']}
style={styles.linearGradient}
>
<View style={styles.container}>
<Navigation />
<Toast ref={(ref) => Toast.setRef(ref)} />
</View>
</LinearGradient>
);
}
Solved it.
For anyone facing the same issue:
NavigationContainer should not be inside View. It should be used as a root component
react-native version 0.62
I am making a quiz app and on every new question(a new quiz screen, QuestionScreen.js), I want to update a label in the navigation bar which is declared in App.js
In App.js, the problem here is I don't know how to pass the state questionIndex to HomeScreen or QuestionScreen, normally I thought this would be using props like <Question questionIndex={questionIndex}>. I tried to pass them in options but that doesn't seem to work...
const App = () => {
const [questionIndex, setQuestionIndex] = useState(0);
return (
<NavigationContainer>
<Stack.Navigator initialRouteName='Home'
>
<Stack.Screen
name='Home'
component={HomeScreen}
options={{
title:''
}}
/>
<Stack.Screen
name='QuestionScreen'
component={QuestionScreen}
options={({ navigation }) => ({
headerBackTitleVisible: false,
headerRight: ()=>(<HeaderRight pageIndex={questionIndex}/>),
headerRightContainerStyle: {
alignItems: "flex-end",
paddingRight: 25
},
})}
/>
function HeaderRight(props) {
return (
<Text>{props.pageIndex}/{questions.length-1}</Text>
);
}
const HomeScreen = ({ navigation }) => {
return (
<View style={styles.container} >
<ScrollView>
<TouchableOpacity onPress={()=>
//here I'm unable to retrieve the questionIndex props.
//tried navigation.getParams("questionIndex") or route.params
navigation.navigate('QuestionScreen', {
pageIndex: 0
})
}>
In QuestionScreen.js
function QuestionScreen({route, navigation, options}) {
const { pageIndex } = route.params;
[Full code here on github]https://github.com/liaoxsong/expofirst
I am a newbie to react-native and I come from native mobile development, this Stack.Navigator api looks weird, can't I just manage the navigation header from the screen component itself? That would make things easier.
it is difficult to customize the default header given by react navigation, what you can do is disabling the header and creating your own custom header.
disable header by giving prop headerMode={"none"} in createStackNavigator and have your own custom header on top,
to disable header docs : https://reactnavigation.org/docs/stack-navigator/#headermode
I have a nested drawer navigator below, I am using a custom component in the header:
header: props => {
return <DrawerHeader {...props} />;
},
When I try and access from props the current route in my header, like below, the title is undefined, how can I get the current route?
render() {
const {
navigation,
videos,
search: {term},
scene: {
route: {routeName: title}, // undefined
},
} = this.props;
return (
<View>
<View style={styles.container}>
Navigator:
function DrawerStack() {
return (
<Drawer.Navigator>
<Drawer.Screen
name="VideoEpisodesScreen"
component={VideoEpisodesScreen}
/>
<Drawer.Screen name="TestYourselfScreen" component={TestYourselfScreen} />
<Drawer.Screen name="MyResultsScreen" component={MyResultsScreen} />
<Drawer.Screen name="AboutScreen" component={AboutScreen} />
<Drawer.Screen name="TestsScreen" component={TestsScreen} />
<Drawer.Screen
name="BookmarkedVideosScreen"
component={BookmarkedVideosScreen}
/>
</Drawer.Navigator>
);
}
export default function AppNavigator() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={stackOptions}
/>
<Stack.Screen
name="Drawer"
component={DrawerStack}
options={drawerOptions}
/>
<Stack.Screen
name="MyResultsScreen"
component={MyResultsScreen}
options={options}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
Funnily enough I had the exact same problem and I found your question after it was just an hour old. Essentially the problem is that React Navigation will only give you the current route of the navigator containing the header. If you have a nested navigator, you won't be able to get it.
It looks like this is somewhat intentional, but I've found that by manually querying the state of the navigator, you can drill down to the "deepest" navigator route. Note that while this works for react-navigation 5, it may not work in the future.
You can iteratively query the nested state like this:
const state = navigation.dangerouslyGetState();
let actualRoute = state.routes[state.index];
while (actualRoute.state) {
actualRoute = actualRoute.state.routes[actualRoute.state.index];
}
Note that this is extremely brittle, but it seems to work good enough for my use cases. You should consider creating an issue/feature request on the react-navigation repository for supporting this use case officially.
React Navigation v6 solution:
A nested route object can be discovered through parent Screen listeners. It's given in a callback argument, and can be passed to getFocusedRouteNameFromRoute to get the route name. In the example shown below, I chose to utilize it during the event 'state' (whenever state changes in the nested stack), but you can use it elsewhere if you want.
<Screen
listeners={({ route }) => ({
state: () => {
const subRoute = getFocusedRouteNameFromRoute(route)
// Your logic here //
}
})}
/>
I think in react-navigation 5, you can access route from this.props
const { route } = this.props;
I'm trying to pass an initialParam prop to a TabNavigator screen. It is meant to be a login piece of local state, just doing a proof of concept. It works fine with the stack navigator screen, but the route.params on the tab screen is undefined. What am I doing wrong?
Also, I'm pretty sure this will have to change as far as state management goes, but I wanted to make sure I could simply change the state in the parent App and have it take effect. It works on the stack screen, meaning I can call setLoggedIn(true) and it will take me to the tab navigator. But I can't go back...
const Track = () => {
return (
<TrackStack.Navigator>
<TrackStack.Screen
name='TrackListScreen'
component={TrackListScreen}
/>
<TrackStack.Screen
name='TrackDetailScreen'
component={TrackDetailScreen}
/>
</TrackStack.Navigator>
);
};
const App = () => {
const [loggedIn, setLoggedIn] = useState(false);
return (
<NavigationContainer>
{loggedIn ? //is logged in
(
<MainTab.Navigator>
<MainTab.Screen
name='TrackCreateScreen'
component={TrackCreateScreen}
initialParams={{ setLoggedIn }}
/>
<MainTab.Screen
name='AccountScreen'
component={AccountScreen}
/>
<MainTab.Screen
name='Track'
component={Track}
/>
</MainTab.Navigator>
) : (
<LoginStack.Navigator>
<LoginStack.Screen
name='SignupScreen'
component={SignupScreen}
initialParams={{ setLoggedIn }}
/>
<LoginStack.Screen
name='SigninScreen'
component={SigninScreen}
/>
</LoginStack.Navigator>
)}
</NavigationContainer>
);
};
export default App;
I'm stuck with React Native.
I have a "Header" navigationBar, but I want to add another navigationBar to my Navigator component.
render() {
let currentRoute = this.props.route
return (
<Navigator
style={styles.container}
initialRoute={this.props.route}
renderScene={this.router.bind(this)}
navigationBar={<Header />} // << There, how can I simply add another navigationBar ?
/>
);
}
And here's the <Header/> component :
render() {
return <Navigator.NavigationBar
style={styles.navBarContainer}
navState={this.props.navState}
routeMapper={routeMapper}
/>
}
Now, I'm trying to add a <Footer/> component, which would render a similar component as <Header/>, in order to have 2 persistent navigation bar on my app.
How to achieve this ?
I also meet this question, and have resolved it. In React Native, it is not supported to add multiple navigationBar. But, if you want to add another "navigationBar", you can add this "navigationBar" as the sibling node of the Navigator, such as:
render() {
return (
<View style={styles.scene}>
<TopStatusBar
showBackIcon={false}
centerText={LocalizedStrings.appName}
rightIcon={require("../../res/icons/head.png")}
onRightPress={this._onHeadPress.bind(this)}
/>
<Navigator
initialRoute={ROUTE_STACK[0]}
renderScene={this._renderScene.bind(this)}
configureScene={() => Navigator.SceneConfigs.FadeAndroid}
navigationBar={
this.state.displayBottomTabBar ?
<BottomTabBar
ROUTE_STACK={ROUTE_STACK}
/>
:
null
}
onWillFocus={(route) => {
this.presentedRoute = route;
}}
sceneStyle={{flex: 1}}
/>
</View>
);
}
In the upper code, TopStatusBar is a composite component. It persists across scene transitions, just like the navigatorBar.
Good luck!