How to select conditional createStackNavigator screen? - react-native

I'm trying to select conditional screen based on Redux data. I have two screens in a screen stack, and this screen stack is also a part of TabNavigator (Logged out screen should be shown with Tabs too). If user is logged in, I need to show CheckScreen, otherwise CheckScreenLoggedOut. I tried setting initialRouteName conditionally but it shows CheckScreenLoggedOut only after logging in.
How can I implement this logic?
Here is my createStackNavigator part:
const CheckScreenStack = createStackNavigator(
{
CheckSendLoggedOut: {
screen: IntroScreen,
navigationOptions: ({navigation}) => ({
headerLeft: (
<View>
<TestModeIcon/>
</View>
),
}),
},
CheckScreen: {
screen: CheckScreen,
navigationOptions: ({navigation}) => ({
headerRight: (
<View>
<TouchableOpacity title='' onPress={() => { navigation.navigate('NotificationsScreen'); }}>
<NotificationTabBarIcon/>
</TouchableOpacity>
</View>
)
}),
},
},
{
initialRouteName: (typeof store.getState().userData !== "undefined") ? 'CheckScreen' : 'CheckSendLoggedOut'
}
);

I have already achieved this by accessing the redux state with connect, you can do something like this:
const CheckScreenStack = ({ userData }) => {
const Stack = createStackNavigator(
{
...
},
{
initialRouteName: (typeof userData !== "undefined") ? 'CheckScreen' : 'CheckSendLoggedOut'
}
);
return Stack;
}
const mapStateToProps = ({ userData }) => {
return { userData };
};
export default connect(
mapStateToProps,
{},
)(CheckScreenStack);

Related

How do I conditionally render a header in a Stack Navigator for React Native Navigation?

I would like my initial page to NOT contain a header. However, I would like a header to appear on each subsequent page. Right now my current stackNavigator looks like this:
const AppNavigator = createStackNavigator(
{
HomeScreen: HomePage,
SecondPage: SecondPage,
Account: Account,
About: About,
},
{
initialRouteName: 'HomeScreen',
headerMode: 'none',
navigationOptions: {
headerVisible: false,
},
},
);
export default createAppContainer(AppNavigator);
Here is the basic boilerplate for my second page:
const SecondPage: () => React$Node = () => {
return (
<>
<StatusBar barStyle="dark-content" />
<View style={styles.body}>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>This is the Secondpage</Text>
</View>
</View>
</>
);
};
export default SecondPage;
You have three ways of doing this using navigationOptions:
1) Directly when you define your navigator:
createStackNavigator({
{
HomeScreen: {
screen : HomePage,
navigationOptions: {header : null}
},
SecondPage: SecondPage,
Account: Account,
About: About,
},
{ ... } //your navigationOptions
})
2) Directly inside the screen as said by Shashank Malviya. Only if you aren't using a functional component.
3) Return defaultNavigationOptions as a function and use navigation state:
createStackNavigator({
{ ... }, //your RouteConfigs
{
initialRouteName: 'HomeScreen',
headerMode: 'none',
defaultNavigationOptions : ({navigation}) => navigation.state.routeName === "HomePage" && {header: null}
},
})
Can be written on every component before render
static navigationOptions = ({ navigation }) => {
const { state } = navigation
return {
headerVisible: true // can be true or false
}
}

Accessing Context Inside of TabNavigator? React Native

I currently have a tab navigator nested inside a stack navigator.
I've been trying to adjust my tabnavigator so it's able to leverage the useContext() hook to conditionally display a tab but haven't had any luck.
Here's my navigation.js file:
const TabNavigator = createBottomTabNavigator(
{
...(myInfo
? {
MyInfo: {
screen: MyInfo,
navigationOptions: {
tabBarIcon: ({ focused }) => {
let source;
focused
? (source = require('../assets/MyInfo_Inactive.png'))
: (source = require('../assets/MyInfo_Inactive.png'));
return <Image style={{ marginTop: 30 }} source={source} />;
},
},
},
}
: {}),
Account: {
screen: Account,
navigationOptions: {
tabBarIcon: ({ focused }) => {
let source;
focused
? (source = require('../assets/Account_Inactive.png'))
: (source = require('../assets/Account_Inactive.png'));
return <Image style={{ marginTop: 30 }} source={source} />;
},
},
},
},
{
defaultNavigationOptions: {
tabBarOptions: {
showLabel: false,
},
},
},
);
const MainStackNavigator = createStackNavigator(
{
Tabs: {
screen: TabNavigator,
},
Login: {
screen: Login,
},
},
{
initialRouteName: 'Login',
headerMode: 'none',
},
);
export default MainStackNavigator;
Where 'myInfo' would come from my context.
and my App.js file:
const AppContainer = createAppContainer(MainStackNavigator);
const App: () => React$Node = () => {
return (
<AccountProvider>
<AppContainer />
</AccountProvider>
);
};
export default App;
Any help is super appreciated as I'm new to React Native and have been trying to find an answer that works for quite some time.
Thanks!

React Navigation drawer doesn't display the title of the current page

I created 2 pages namely Home and Profile and added these two pages to createDrawerNavigation. And then added this drawer navigation to a stack navigator. Now when I try to display the title on the header of the current page. I'm unable to do it.
Here is my code.
Tried DefaultNavigationOptions and got the props value of navigator but doesn't work that way.
const MenuStack = createDrawerNavigator(
{
HomePage: {
screen: Home
},
ProfilePage: {
screen: Profile
}
},
{
drawerWidth: Math.round(Dimensions.get("window").width) * 0.75,
contentOptions: {
activeTintColor: theme.colors.accent
}
}
);
const AppStack = createStackNavigator(
{
MenuStack: {
screen: MenuStack
}
},
{
defaultNavigationOptions: ({ navigation }) => {
return {
headerLeft: (
<Icon
style={{ paddingLeft: 10 }}
onPress={() => navigation.toggleDrawer()}
name="bars"
size={30}
/>
),
headerRight: (
<Icon
style={{ paddingRight: 10 }}
onPress={() => navigation.toggleDrawer()}
name="search"
size={30}
/>
),
headerTitle: navigation.state.routeName,
headerStyle: {
backgroundColor: theme.colors.accent
}
};
}
}
);
export const createRootNavigator = (signedIn = false) => {
return createAppContainer(
createSwitchNavigator(
{
SignedIn: {
screen: AppStack
},
SignedOut: {
screen: WelcomeStack
},
SignInAndOut: {
screen: AuthStack
}
},
{
initialRouteName: signedIn ? "SignedIn" : "SignedOut"
}
)
);
};
I added a headerTitle property in the createStackNavigator but I tell the "menustack" as the title for every page.
Expected the title of the page on the header.
You have 2 options:
1. Render your own header component in each screen
You can render a header inside your screens, either a custom one or one from a component library such as react native paper.
function Home() {
return (
<React.Fragment>
<MyCustomHeader title="Home" />
{{ /* screen content */ }}
</React.Fragment>
);
}
2. Create a stack navigator for each screen that needs to have header
const MenuStack = createDrawerNavigator({
HomePage: {
screen: createStackNavigator({ Home })
},
ProfilePage: {
screen: createStackNavigator({ Profile })
}
});
Keep in mind that while this requires less code, it's also less performant.

React native navigation parameter not working with Switch navigator

I'm using react native navigation and redux in my code.
Below is my navigation structure
const MainTabNavigator = createBottomTabNavigator({
Home,
Tenants,
WorkOrders: {
screen: WorkOrders,
navigationOptions: {
title: 'Work Orders',
headerTitle: 'Work Orders'
}
}
}, {
navigationOptions: ({ navigation }) => {
const { routeName } = navigation.state.routes[navigation.state.index];
return {
headerTitle: routeName
};
}
});
const MainStackNavigator = createStackNavigator({
MainTabNavigator
}, {
defaultNavigationOptions: ({ navigation }) => {
return {
headerLeft: (
<Icon
style={{ paddingLeft: 10 }}
onPress={() => navigation.openDrawer()}
name="md-menu"
size={30}
/>
)
};
}
});
const AppDrawerNavigator = createDrawerNavigator({
Menu: {
screen: MainStackNavigator
}
});
const AppSwitchNavigator = createSwitchNavigator({
Login: { screen: Login },
Main: { screen: AppDrawerNavigator }
});
Then, I have an action file where I navigate from the Login to the Main pages.
async
...
console.debug(response.data); // Here the console shows value 10
NavigationService.navigate('Main', { userID: response.data });
In the HomeScreen page I tried to see the parameter value:
const { navigation } = this.props;
const userID = navigation.getParam('userID', '0');
console.debug(userID); // HERE THE VALUE IS 0, BUT SHOULD BE 10
What I am doing wrong? Thanks
You can't pass params with a SwitchNavigator, it's clearly intended in the doc:
By default, it does not handle back actions and it resets routes to
their default state when you switch away.
If you really need to share objects between screens, you should use redux rather than react-navigation.

Re Render Tab Navigator onPress React Navigation

when i'm using stack navigator only. the screen get re render every time when i navigate to another screen. so how to do the same thing with tab navigator? every time i press the tab menu(favorite)?
Screenshot:
Code:
const RootStack = StackNavigator(
{
Home: {
screen: Home,
navigationOptions: {
header: null
}
},
Menu: {
screen: Menu,
navigationOptions: {
header: null
}
},
}
);
export default TabNavigator(
{
Home: { screen: RootStack },
Favorite: { screen: Favorite },
},
{
navigationOptions: ({ navigation }) => ({
tabBarIcon: ({ focused, tintColor }) => {
const { routeName } = navigation.state;
let iconName;
if (routeName === 'Home') {
iconName = `home`;
} else if (routeName === 'Favorite') {
iconName = `heart`;
}
return <Icon name={iconName} size={25} color={tintColor} />;
},
}),
tabBarComponent: TabBarBottom,
tabBarPosition: 'bottom',
tabBarOptions: {
activeTintColor: '#00a6ed',
inactiveTintColor: '#9e9e9e',
style: {
backgroundColor: '#FFFFFF',
},
},
animationEnabled: false,
swipeEnabled: false,
}
);
Thanks in advance!
There are 2 different ways I found below,
// call when the screen is focused
componentDidMount () {
this._navListener = this.props.navigation.addListener('didFocus', (payload) => {
// update based on your requirements
});
}
OR
import { NavigationEvents } from "react-navigation";
...
class HomeScreen extends React.Component {
render() {
return (
<View>
<NavigationEvents
onWillFocus={() => {
// update based on your requirements!
}}
/>
<Text>Home</Text>
</View>
);
}
}
I recommend using Hooks to re-render for the screen when it changes focus, The library exports a useIsFocused hook to make this easier
isFocused is method lets us check whether the screen is currently focused. Returns true if the screen is focused and false otherwise.
import { useIsFocused } from '#react-navigation/native';
import { Text } from '#react-native';
function Home() {
const isFocused = useIsFocused();
return <Text>{isFocused ? 'focused' : 'unfocused'}</Text>;
}
If you are using class component you can wrap your class component in a function component
import { useIsFocused } from '#react-navigation/native';
class Home extends React.Component {
render() {
// Get it from props
const { isFocused } = this.props;
}
}
// Wrap and export
export default function(props) {
const isFocused = useIsFocused();
return <Home {...props} isFocused={isFocused} />;
}
react-navigation supports listeners which you can use to detect focus or blur state of screen.
addListener - Subscribe to updates to navigation lifecycle
React Navigation emits events to screen components that subscribe to
them:
willBlur - the screen will be unfocused
willFocus - the screen will focus
didFocus - the screen focused (if there was a transition, the transition completed)
didBlur - the screen unfocused (if there was a transition, the transition completed)
Example from the docs
const didBlurSubscription = this.props.navigation.addListener(
'didBlur',
payload => {
console.debug('didBlur', payload);
}
);
// Remove the listener when you are done
didBlurSubscription.remove();
// Payload
{
action: { type: 'Navigation/COMPLETE_TRANSITION', key: 'StackRouterRoot' },
context: 'id-1518521010538-2:Navigation/COMPLETE_TRANSITION_Root',
lastState: undefined,
state: undefined,
type: 'didBlur',
};