Creating a TabNavigator with dynamic tabs

DerpyNerd picture DerpyNerd · Sep 22, 2017 · Viewed 12.2k times · Source

I'm relatively sure I found out it isn't possible, but I want to make sure there isn't a way.

The app in question starts off with an AppNavigator StackNavigator.

export const AppNavigator = StackNavigator({
    Login: {
        screen: Login,
        navigationOptions: ({navigation}) => ({
            title: 'Aanmelden',
            params: {
                nuke: navigation.state.params && !!navigation.state.params.nuke,
            },
        }),
    },
    Main: {
        screen: DynamicTabBar,
        navigationOptions: ({navigation}) => ({
            title: 'Follow-up',
        }),
    },
}, {
    mode: 'modal',
    headerMode: 'none',
});

export class AppWithNavigationState extends React.Component {
    constructor(props) {
        super(props);
    }

    render() {
        return <AppNavigator navigation={addNavigationHelpers({ dispatch: this.props.dispatch, state: this.props.navigationReducer })} />
    }
}

AppWithNavigationState.propTypes = {
    dispatch: React.PropTypes.func.isRequired,
    navigationReducer: React.PropTypes.object.isRequired,
};

const mapStateToProps = state => ({
    navigationReducer: state.navigationReducer,
});

export default connect(mapStateToProps)(AppWithNavigationState);

So far so good, it's just that the DynamicTabBar is should not be 'rendered' until the user has logged in (i.e. navigating from Login to Main).

Here's why

const Tabs = TabNavigator({
        Start: {
            screen: UserStackNavigator,
            navigationOptions: {
                tabBarIcon: (<Icon
                    type="font-awesome"
                    name="users"
                    color="#dddddd"
                    size={20}
                />),
            },
        },
        ...Account.User.CanEnter ? {
            ConditionalTab: {
                screen: ConditionalScreen,
                navigationOptions: {
                    tabBarIcon: (<Icon
                        type="font-awesome"
                        name="recycle"
                        color="#dddddd"
                        size={20}
                    />),
                },
            }} : {},
        Settings: {
            screen: Settings,
            navigationOptions: {
                tabBarIcon: (<Icon
                    type="font-awesome"
                    name="cog"
                    color="#dddddd"
                />),
            }
        }
    },{
        ...TabNavigator.Presets.AndroidTopTabs,
        tabBarPosition: "bottom",
        tabBarOptions: {
            activeTintColor: '#eaeb65',
            showIcon: true,
            showLabel: false,
            style: { backgroundColor: '#333' },
        }
    });

export default class DynamicTabBar extends React.Component {
    constructor(props) {
        super(props);
    }
    render() {
        return <Tabs navigation={this.props.navigation} />;
    }
}
DynamicTabBar.router = Tabs.router;

...Account.User.CanEnter ? { is always false because the TabNavigator is rendered before the user has logged in and Account is filled. A failed attempt, it seams.

I wanted to populate Tabs inside componentWillMount, but then I can't set the static router: DynamicTabBar.router = Tabs.router;

Any ideas on how to fix this?

Answer

DerpyNerd picture DerpyNerd · Sep 23, 2017

Yessssss! I think I found a way. The solution probably isn't following the design pattern, but this is what I came up with:

export default class DynamicTabBar extends React.Component {
    static router = TabRouter({
        Start: {
            screen: UserStackNavigator,
            navigationOptions: {
                tabBarIcon: (<Icon
                    type="font-awesome"
                    name="users"
                    color="#dddddd"
                    size={20}
                />),
            },
        },
        ...Account.User.CanEnter ? {
            ConditionalTab: {
                screen: ConditionalScreen,
                navigationOptions: {
                    tabBarIcon: (<Icon
                        type="font-awesome"
                        name="recycle"
                        color="#dddddd"
                        size={20}
                    />),
                },
            }} : {},
        Settings: {
            screen: Settings,
            navigationOptions: {
                tabBarIcon: (<Icon
                    type="font-awesome"
                    name="cog"
                    color="#dddddd"
                />),
            }
        }
    },{
        ...TabNavigator.Presets.AndroidTopTabs,
        tabBarPosition: "bottom",
        tabBarOptions: {
            activeTintColor: '#eaeb65',
            showIcon: true,
            showLabel: false,
            style: { backgroundColor: '#333' },
        }
    });
    constructor(props) {
        super(props);
    }

    render() {
        const tabs = TabNavigator({
            Start: {
                screen: UserStackNavigator,
                navigationOptions: {
                    tabBarIcon: (<Icon
                        type="font-awesome"
                        name="users"
                        color="#dddddd"
                        size={20}
                    />),
                },
            },
            ...Account.User.CanEnter ? {
                ConditionalTab: {
                    screen: ConditionalScreen,
                    navigationOptions: {
                        tabBarIcon: (<Icon
                            type="font-awesome"
                            name="recycle"
                            color="#dddddd"
                            size={20}
                        />),
                    },
                }} : {},
            Settings: {
                screen: Settings,
                navigationOptions: {
                    tabBarIcon: (<Icon
                        type="font-awesome"
                        name="cog"
                        color="#dddddd"
                    />),
                }
            }
        },{
            ...TabNavigator.Presets.AndroidTopTabs,
            tabBarPosition: "bottom",
            tabBarOptions: {
                activeTintColor: '#eaeb65',
                showIcon: true,
                showLabel: false,
                style: { backgroundColor: '#333' },
            }
        });

        return <Tabs navigation={this.props.navigation} />;
    }
}

The router is assigned in a static way and is built dynamically at runtime.