Conditional routing using React Navigation (React Native) - reactjs

Building a React Native app, and I would to send a user to the next screen DEPENDING on the initial Select screen's state. So, if there there is 1 item selected, it should take the user to the Single screen. If more than 1, the Multiple screen.
As you can see, I made custom headerLeft and headerRight components. I would prefer to keep the routing this way, but somehow add the initial state into this context. Is there a way?
If I could somehow decouple the navigation options from the routes.js file and put them into the Screen components themselves, that would be awesome. But from what I researched, it doesn't seem like that the navigationOptions can directly access state.
Here's the relevant code (routes.js):
export default StackNavigator(
{
Select: {
screen: Select,
navigationOptions: ({ navigation }) => ({
headerLeft: <View />,
headerRight: (
<HeaderRight
navigation={navigation}
destination=""
showIcon
/>
)
})
},
Single: {
screen: Single,
navigationOptions: ({ navigation }) => ({
headerLeft: (
<HeaderLeft navigation={navigation} destination="back" showIcon />
),
headerRight: (
<HeaderRight navigation={navigation} destination="SingleSecond" />
)
})
},
Multiple: {
screen: Multiple,
navigationOptions: ({ navigation }) => ({
headerLeft: <HeaderLeft navigation={navigation} destination="back" />,
headerRight: (
<HeaderRight navigation={navigation} destination="MultipleSecond" />
)
})
},
},
{
headerMode: "float",
navigationOptions: ({ navigation }) => ({
title: `${navigation.state.routeName}`,
headerStyle: { backgroundColor: "#000" },
headerTitleStyle: {
color: "#fff",
textAlign: "center",
alignSelf: "center"
}
})
}
);

you can set params from a component these params can be accessed from navigation options.
this.props.navigation.setParams({ selectionState: "single" });
this.props.navigation.setParams({ selectionState: "multiple" });
and you can access the params from the navigation options like
static navigationOptions = ({navigation}) => {
let selected = navigation.state.params && navigation.state.params.selectionState;
return {
headerRight: ...
};
}
I hope this fills your needs, you need to set params after selecting something in your component. There is another way to dynamically set the initial state.
function getNavigation (selected = "single") {
return StackNavigator({
...,
{ initialState = selected === "single" ? "Single" : "Multiple" }
})
}
and then call this method with the selected argument to reset the screen.

Related

Main Navigation 3 (Drawer and Stack) together problem

I am using react navigation version 3
I trying to use Drawer and Stack together
I am implementing a navigation drawer and works well
But when I use stack navigation I can't see the back button option in the header
Drawer -> ListScreen ->onpress item open stack navigation to view profile ->
here I can't go back to ListScreen
I tried edit navigation option and add custom properties but still, I don't understand why it's not visible
const AuthStackNavigation = createStackNavigator({
HomeStack: {
screen.
screen: LeadScreen,
headerBackTitleVisible:true,
navigationOptions: ({ navigation }) => ({
headerLeft: <Icon
name='arrow-back' onPress={ () => { navigation.goBack() }} style={{width:100}} />
,
title: `${navigation.state.params.name}'s Profile'`,
headerStyle: {
backgroundColor: '#f4511e',
},
headerTitleStyle: { flex: 1, textAlign: 'center'},
}),
},
});
//navigate('Profile', { name: 'Brent' })
const AppStackNavigator = createDrawerNavigator({
'בית':HomeScreen,
'רשימת לידים':LeadsScreen,
'ניהול סוכנים':ManageUsersScreen
,
'הגדרות':SettingsScreen
},{
contentComponent:CustomerDrawerComponent
}
)
const MainNavigation = createSwitchNavigator({
HomeDrawer: AppStackNavigator,
AuthStack: AuthStackNavigation, // You will use this.props.navigation.replace('HomeDrawer') after login process.
})
export default createAppContainer(MainNavigation);

onpress event for icon in TabNavigator (react native with react-navigation)

I have an icon that i include in a tab navigator as per below:
const SearchTabBarIcon = ({ tintColor }) => (
<IconMCI
name="account-search"
size={45}
color={tintColor}
onPress={() => console.log('HELP!!')}
/>
);
....then my tabNavigator looks like this:
const SignedInTabNav = TabNavigator(
{
Profile: {
screen: Profile,
navigationOptions: {
tabBarLabel: 'Me',
tabBarIcon: ProfileTabBarIcon,
},
},
Search: {
screen: Search,
navigationOptions: {
tabBarLabel: 'Search',
tabBarIcon: SearchTabBarIcon,
},
},
tabBarComponent: TabBarBottom,
animationEnabled: false,
},
);
The console.log is fired when I click the icon, however the native functionality of the tabNavigator is lost.
How can I fire the onPress event but also maintain the navigation functionality?
Ive heard I should perhaps "add the props of navigation to my component in the render".
The render function of App.js looks like this:
render() {
const Layout = createRootNavigator(this.props.isAuthenticated);
return (
<View style={{ flex: 1, backgroundColor: '#F8F8FF' }}>
<Layout />
</View>
);
}
....and the function createRootNavigator looks like this
const createRootNavigator = (authenticated = false) =>
createSwitchNavigator(
{
SignedInTabNav: {
screen: SignedInTabNav,
},
SignedOutStackNav: {
screen: SignedOutStackNav,
},
},
{
initialRouteName: authenticated ? 'SignedInTabNav' : 'SignedOutStackNav',
},
);
....so where/how do I add the navigation prop in the render for App.js?...and will it be passed down through createRootNavigator.....and further through createSwitchNavigator....and finally passed into SignedInTabNav (TabNavigator) where the Search icon is referenced?
You can simply use tabBarOnPress callback to handle press events as an additional option, like this:
const SignedInTabNav = TabNavigator(
{
Profile: {
screen: Profile,
navigationOptions: {
tabBarLabel: 'Me',
tabBarIcon: ProfileTabBarIcon,
},
},
Search: {
screen: Search,
navigationOptions: {
tabBarLabel: 'Search',
tabBarIcon: SearchTabBarIcon,
tabBarOnPress: ({ navigation, defaultHandler }) => {
console.log('this will be fired just before nagivation happens')
defaultHandler() // if you omit this, navigation will not happen
}
},
},
tabBarComponent: TabBarBottom,
animationEnabled: false,
},
);
Solution was to use the 'didFocus' event from reactNavigation
https://reactnavigation.org/docs/en/function-after-focusing-screen.html
componentDidMount() {
const { navigation } = this.props;
this.focusListener = navigation.addListener('didFocus', () => {
// The screen is focused
// Call any action
});
}

react native how to re render refresh screen on tabBarOnPress

the screen do not get the new data until i close the app and open it again
i want to re-render the screen on tab press to refresh the screen and get the new data
Please see my below code
const DashboardTabNavigator = createBottomTabNavigator({
Home: { screen: Home,
navigationOptions: {
tabBarIcon: (
<Image style={{ width: 30, height: 30,tintColor:'white' }}
source={require('./assets/IconBootomTabNavigation/home_icon.png')} />
)
}
},
Category: { screen: Category,
navigationOptions: {
tabBarIcon: (
<Image style={{ width: 30, height: 30,tintColor:'white' }}
source={require('./assets/IconBootomTabNavigation/cat_icon.png')} />
)
}
}
I recommend you to place the code responsible for fetching the data inside componentDidMount lifecycle method. This way you can ensure you get the data after the initial render.
If the screens are not being re-rendered, please check this thread. You can use tabBarOnPress prop to call any component method.
You might also try this solution:
static navigationOptions = ({navigation}) => {
return {
tabBarOnPress: (tab, jumpToIndex) => {
if(!tab.focused) {
jumpToIndex(tab.index);
navigation.state.params.onFocus()
}
},
}
}
componentDidMount() {
this.props.navigation.setParams({
onFocus: your-data-fetching-method
})
}

Override navigation bar title

I want to override the title on the navigation bar after the navigation has happened.
StackNavigator
const DashboardNavigator = new StackNavigator({
Dashboard: {
screen: HomeScreen,
navigationOptions: {
title: 'Home'
}
}
}, {
navigationOptions
});
I want to basically rename the title to something else when I got to the Home page. I know it's weird but need to handle dynamic titles.
This is what I have tried (HomeScreen separate component):
componentDidMount() {
this.props.navigation.setParams({
navigationOptions: {
title: 'New title'
}
});
}
This isn't making a difference. Any suggestions?
Also worth mentioning that I'm using redux.
You can try this:
StackNavigator
const DashboardNavigator = new StackNavigator({
Dashboard: {
screen: HomeScreen,
}
}, {
navigationOptions
});
HomeScreen In your homeScreen check if you have a dynamic title, if not set the title to "Home" but if exists set it to your dynamic one.
static navigationOptions = ({ navigation }) => ({
title: navigation.state.params ==='undefined' || navigation.state.params.title === 'undefined' ? 'Home': navigation.state.params.title
});
And then set your dynamic title like this:
componentDidMount() {
this.props.navigation.setParams({ title: 'your new title' })
}
in your home screen try this
Home Screen
static navigationOptions = ({ navigation }) => {
return {
title: navigation.state.params.headerTitle,
headerRight: (
<Icon
name="calendar"
type="octicon"
color="#fff"
/>
),
headerLeft: (
<Icon
name="keyboard-arrow-left"
color="#fff"
onPress={() => navigation.goBack(null)}
/>
),
headerStyle: {
backgroundColor: "#000",
paddingLeft: 10,
paddingRight: 10
},
headerTitleStyle: { color: "#fff", alignSelf: "center" }
};
};
componentDidMount() {
this.props.navigation.setParams({
headerTitle: 'New title'
});
}

States in TabStackNavigator?

It seems like TabNavigator doesn't have it's own state. Is there any way to use state or props?
I want to show the number of unread notification on the Notification TabIcon.
export default TabNavigator(
{
...
Noti: {
screen: NotificationStackNavigator,
},
...
},
{
navigationOptions: ({ navigation }) => ({
header: null,
tabBarIcon: ({ focused }) => {
const { routeName } = navigation.state;
let iconName;
switch (routeName) {
...
case 'Noti':
iconName = 'ios-notifications';
break;
...
}
...
if(iconName == 'ios-notifications') {
return (
<IconBadge
MainElement={
<Ionicons
name={iconName}
size={30}
style={{ marginBottom: -3 }}
color={focused ? Colors.tabIconSelected : Colors.tabIconDefault}/>
}
BadgeElement={
<Text style={{color:'#FFFFFF'}}>
{{this.state.notifications}} // my goal
</Text>
}
IconBadgeStyle={{
backgroundColor: '#f64e59',
position: 'absolute',
right:-10,
top:0,
}}
/>
);
}
...
Please let me know if anything is unclear. Thanks in advance
UPDATE I'm planning to refactor my TabNavigator. Is this what you're trying to say?
export default TabNavigator(
to
const MainTabNavigator = TabNavigator(
class MainTabNavigator extends Component {
state = {notification}
export default connect(...)(MainTabNavigator);
UPDATE 2 MainTabNavigator's Top Level component is another TabNavigator. Is there any other solution?
const RootTabNavigator = TabNavigator ({
Auth: {
screen: AuthStackNavigator,
},
Welcome: {
screen: WelcomeScreen,
},
Main: {
screen: MainTabNavigator,
},
You can pass additional custom props via screenprops
const TabNav = TabNavigator({
// config
});
<TabNav
screenProps={/* this prop will get passed to the screen components as this.props.screenProps */}
/>
The full documentation is here

Resources