React Native - 使用React Navigation动态创建Navigator

时间:2020-12-20 18:59:42

I am building a mobile application with React Native and I am using React Navigation to build a navigator inside my application. React navigation provided me a good way to handle nested Tab bars inside a drawer which is also inside a Stack Navigator.

我正在使用React Native构建移动应用程序,我正在使用React Navigation在我的应用程序中构建导航器。 React导航为我提供了一个很好的方法来处理抽屉内的嵌套Tab栏,它也在Stack Navigator中。

The problem is that I need to specify components so that I can provide these into the Tab Bar. Lets say we have to fetch some categories from an API and we do not know how many categories are inside the data. Besides, I could not figure out that even if I try to fetch data at start, the navigator and redux configuration takes place at start which means the application has to know the components in those tab navigators. I could not figure out that even if I fetched the data from the API, how I can create multiple components and while stopping the application configuration.

问题是我需要指定组件,以便我可以将它们提供到Tab栏中。让我们说我们必须从API中获取一些类别,我们不知道数据中有多少类别。此外,我无法弄清楚即使我尝试在启动时获取数据,导航器和redux配置也会在启动时发生,这意味着应用程序必须知道这些选项卡导航器中的组件。我无法弄清楚即使我从API获取数据,我如何创建多个组件并停止应用程序配置。

The code below, just demonstrates how I implemented the tab bar. This code works in index.js because as I mentioned before, the application have to know the components inside the Navigator.

下面的代码,演示了我如何实现标签栏。此代码在index.js中有效,因为正如我之前提到的,应用程序必须知道Navigator中的组件。

const TabStack = TabNavigator({
    Food: { screen: FoodStack},
    Drink : { screen: DrinkStack },
    HealthCare : { screen: SnackProducts },
    Snacks: { screen: SnackStack },
},
    {
        tabBarComponent : props => <CustomTabItems props={props}/>,
        tabBarPosition: 'top',
        animationEnabled : true,
        initialRouteName : 'Food',
        swipeEnabled: true,
        tabBarOptions : {
 
            scrollEnabled : true
        }
})

Thanks

谢谢

here the root code

这里是根代码

import { AppRegistry } from 'react-native';
import React from 'react';

import { Text, Image, ScrollView, View, List, ListItem, TouchableWithoutFeedback } from 'react-native';
import { Icon, Avatar } from 'react-native-elements';

import { Provider, connect } from 'react-redux'
import thunkMiddleware from 'redux-thunk'
import { createStore, applyMiddleware, combineReducers } from 'redux';
import {
    addNavigationHelpers, StackNavigator,
    DrawerNavigator,
    DrawerItems,
    TabNavigator,
    TabView,
    TabBarTop,
    NavigationActions
} from 'react-navigation';

// importing starting screen 

import StartingContainer from './src/containers/StartingScreen/StartingContainer';

// Menu Containers
import MenuCredentials from './src/containers/MenuCredentials';

// Containers
import LoginContainer from './src/containers/LoginContainer';
import PhoneNumberValidation from './src/containers/SubLoginContainers/PhoneNumberValidation';
import MainOrderContainer from './src/containers/OrderContainers/MainOrderContainer';
import MainCartContainer from './src/containers/CartContainers/MainCartContainer';

// Components 
// Login Components
import SMSLogin from './src/containers/SubLoginContainers/SMSLogin';

// Profil Components
import Profil from './src/components/ProfileComponents/Profile';
import AdressComponent from './src/components/ProfileComponents/AdressComponent';
import SettingsComponent from './src/components/ProfileComponents/SettingsComponent';
import creditCardComponent from './src/components/ProfileComponents/creditCardComponent';
// Reducers
import initialReducer from './src/reducers/initialReducer';
import cartReducer from './src/reducers/cartReducer';
import starterReducer from './src/reducers/starterReducer';

// import tab bar containers

import FoodProducts from './src/containers/TabBarContainers/FoodProducts';
import HealthProducts from './src/containers/TabBarContainers/HealthProducts';
import SnackProducts from './src/containers/TabBarContainers/SnackProducts';

// Building Navigation
import MenuItem from './src/containers/MenuItemContainer/MenuItem';
import CustomTabItems from './src/containers/CustomTabItems';
import CustomSubTabItems from './src/containers/CustomSubTabItems';

import DrawerButton from './src/containers/DrawerButton';

// Tab Bar Navigation
const ChocolateStack = TabNavigator({
    Tadelle: { screen: MenuItem},
    Milka: { screen: MenuItem},
},
    {
        tabBarComponent : props => <CustomTabItems props={props}/>,
        tabBarPosition: 'top',
        animationEnabled : true,
        initialRouteName : 'Tadelle',
        swipeEnabled: true,
        tabBarOptions: {
            scrollEnabled: true
        },

})
const SnackStack = TabNavigator({
    Çikolatalar: { screen: MenuItem},
    Gofretler: { screen: MenuItem},
    Krakerler: { screen: MenuItem},
    Bisküviler: { screen: MenuItem},
    Kuruyemişler: { screen: MenuItem},
},
    {
        tabBarComponent : props => <CustomSubTabItems props={props}/>,
        tabBarPosition: 'top',
        animationEnabled : true,
        initialRouteName : 'Çikolatalar',
        swipeEnabled: true,
        tabBarOptions : {
            scrollEnabled : true
        }
})

const DrinkStack = TabNavigator({
    'Gazlı İçecekler': { screen: MenuItem},
    'Soğuk Çaylar': { screen: MenuItem},
    'Alkol': { screen: MenuItem},
    'Süt Ürünleri': { screen: MenuItem},
    'Spor İçecekleri': { screen: MenuItem},
},
    {
        tabBarComponent : props => <CustomSubTabItems props={props}/>,
        tabBarPosition: 'top',
        animationEnabled : true,
        initialRouteName : 'Alkol',
        swipeEnabled: true,
        tabBarOptions : {
            scrollEnabled : true
        }
})

const FoodStack = TabNavigator({
    Sandviç : { screen: MenuItem},
    Çorba: { screen: MenuItem},
},
    {
        tabBarComponent : props => <CustomSubTabItems props={props}/>,
        tabBarPosition: 'top',
        animationEnabled : true,
        initialRouteName : 'Sandviç',
        swipeEnabled: true,
        tabBarOptions : {
            scrollEnabled : true
        }
})


const TabStack = TabNavigator({
    Food: { screen: FoodStack},
    Drink : { screen: DrinkStack },
    Health : { screen: SnackProducts },
    Snacks: { screen: SnackStack },
},
    {
        tabBarComponent : props => <CustomTabItems props={props}/>,
        tabBarPosition: 'top',
        animationEnabled : true,
        initialRouteName : 'Food',
        swipeEnabled: true,
        tabBarOptions : {
            tabStyle : {
                width : 250
            },
            scrollEnabled : true
        }
})




// cart navigation will be drawernavigator and drawerItems will be custom !!
const CartNavigation = StackNavigator({
    Cart: {
        screen: MainCartContainer,
    }
},
    {
        headerMode: 'float',
        navigationOptions: ({ navigation }) => ({
            title: 'Sepet',
            headerLeft: <Icon
                name='arrow-back'
                color='#517fa4'
                onPress={() => navigation.navigate('drawerStack')}
            />,
            headerRight:
            <Icon
                name='payment'
                color='#517fa4'
                onPress={() => navigation.navigate('drawerStack')}
            />
        })

    }
)
const DrawerStack = DrawerNavigator({
    Sipariş: { screen: TabStack },
    Profil: { 
        screen: Profil ,
        navigationOptions : ({ navigation }) => ({
            title : 'Profilim',
        })
    },
    Adreslerim: {
        screen: AdressComponent,
        navigationOptions: ({ navigation }) => ({
            title: 'Teslimat Adreslerim'
        })
    },
    Ayarlar: { screen: SettingsComponent }
},
    {
        drawerPosition: 'left',
        headerMode : 'none',
        navigationOptions: ({ navigation }) => ({
            headerStyle: { backgroundColor: '#87CEFA' },
            headerRight: <Icon
            name='shopping-cart'
            color='#517fa4'
            onPress={() => navigation.navigate('cartStack')}
            />,
        }),
        contentOptions: {
            inactiveTintColor: 'white',
            activeTintColor: 'purple',
            style: {
                marginTop: 80,
                marginLeft: 25,
            }
        },
        contentComponent: props => <MenuCredentials {...props} />
})

const DrawerNavigation = StackNavigator({
    DrawerStack: {
        screen: DrawerStack
    }},
   {
       style : {
           leftDrawerWidth : 40
       },
       index : 0,
       navigationOptions : ({ navigation }) => ({
            headerStyle: { backgroundColor: '#87CEFA' },
            gesturesEnabled : false,
            headerRight : <Icon
            name='shopping-cart'
            color='#517fa4'
            onPress={() => navigation.navigate('cartStack')}
            />,
            headerLeft: <Icon
            name='menu'
            color='#517fa4'
            onPress={() => {
                console.log(navigation.state.routes[0]);
                navigation.navigate({
                    key : null,
                    index : 0,
                    action : [
                        navigation.navigate('DrawerToggle')
                    ]
                })
            }}
            />
       }),
       initialRouteParams : {
           name : 'Welcome'
       }
   } 
)

const LoginStack = StackNavigator({
    Login: {
        screen: LoginContainer,
        navigationOptions: ({ navigation }) => ({
            title: ' GİZLİ UYGULAMA ! '
        })
    },
    Ss: {
        screen: SMSLogin,
        navigationOptions: ({ navigation }) => ({
            title: ' SMS ONAYI '
        })
    },
    PhoneNumberValidation: {
        screen: PhoneNumberValidation,
        navigationOptions: ({ navigation }) => ({
            title: 'Kaydolma'
        })
    },
    },{
        headerMode : 'none',
        initialRouteName : 'Login'
})

// IMPORTANT NOTE ***!!!
// CARRY drawerStack to the PrimaryNavigator !!
// CHANGE LoginContainer so that it will navigate to the drawerStack
// NOT FROM ACTION BUT FROM COMPONENT INSIDE COMPONENTWILLUPDATE
// BY CHANGING isAuth variable in initialReducer !!

const PrimaryNavigator = StackNavigator({
    loginStack: {
        screen: LoginStack
    },
    cartStack: {
        screen: CartNavigation
    },
    drawerStack: {
        screen:  DrawerNavigation
    },
    starter : {
        screen : StartingContainer
    }
},
    {
        headerMode: 'none',
        title: 'Main',
        initialRouteName : 'starter'
    }
)

const navReducer = (state, action) => {
    const nextState = PrimaryNavigator.router.getStateForAction(action, state);
    // Simply return the original `state` if `nextState` is null or undefined.
    return nextState || state;
};

// combining Reducers
const AppReducer = combineReducers({
    initialR: initialReducer,
    cartR: cartReducer,
    starterR : starterReducer,
    nav: navReducer
})

// Creating redux store

const store = createStore(
    AppReducer,
    applyMiddleware(thunkMiddleware)
)

// Navigation initilizator to App
class App extends React.Component {
    render() {
        return (
            <PrimaryNavigator navigation={addNavigationHelpers({
                dispatch: this.props.dispatch,
                state: this.props.nav
            })}
            />
        )
    }
}
const mapStateToProps = (state) => ({
    nav: state.nav
})

const AppWithNavigationState = connect(mapStateToProps)(App);

class brilliantApp extends React.Component{
    render(){
        return(
            <Provider store={store}>
            < AppWithNavigationState />
        </Provider>
        )        
    }
}

AppRegistry.registerComponent('brilliantApp', () => brilliantApp);

2 个解决方案

#1


3  

Your TabStack file:

您的TabStack文件:

const CATEGORIES = {
  "Food": { screen: FoodStack },
  // ...
}

export default (screenNames) => {
  const screens = screenNames.reduce((total, name) => ({...total, [name]: CATEGORIES[name]}), {})
  const TabStack = TabNavigator(screens,
    {
        tabBarComponent : props => <CustomTabItems props={props}/>,
        tabBarPosition: 'top',
        animationEnabled : true,
        initialRouteName : 'Food',
        swipeEnabled: true,
        tabBarOptions : {

            scrollEnabled : true
        }
  })
  return TabStack
}

Your Root file:

你的根文件:

import getTabStack from './TabStack'


class Root extends Component {

    state = {
      categoriesNames: null
    }

    componentWillMount() {
      // assuming result is ["Food", "Drink", ... ]
      Api.fetchCategories().then((result) => {
        this.setState({ categoriesNames: result })
      })
    }

    render() {
      const { categoriesNames } = this.state
      if (!categoriesNames) {
        return <SplashScreen />
      }
      const TabStack = getTabStack(categoriesNames)
      return (
        <Provider store={store} >
          <TabStack />
        </Provider>
      );
    }
  }

#2


1  

Here I would like to post a method for creating tab bar according to the data we fetched from some API etc programmatically.

在这里,我想根据我们从一些API等以编程方式提取的数据发布一个创建标签栏的方法。

Here we fetch the data from API in this example, this code from the top level component :

这里我们从这个例子中的API中获取数据,这个代码来自*组件:

renderShopTab() {
        const { client } = this.props;
        try {
            const { categories } = client.readQuery({
                query: gql`
                {
                    categories{
                        id
                        name
                        products{
                            id
                            name
                            price
                            quantity
                        }
                    }
                }`
            })

            console.log("Categories  :" + categories);

            return (
                <ShopCreator categories={categories} />
            )

        } catch (error) {
            console.log("Error occured creating the categories due to the : " + error);

            return (
                <View>
                    <Text>
                        Loading...
                    </Text>
                </View>
            )

        }

    }

This code snippet is from creator of the tab bar dynamically :

此代码段动态地来自标签栏的创建者:

export const ShopCreator = ({ categories }) => {

    // This script will create a TabNavigator for categories and StackNavigators for each member of the categories !

    let categoryStack = {};

    let routes = {};


    categories.forEach((category) => {

        if (category.products.length > 0) {

            const { catname } = category.name;

            if (category.name != undefined) {

                routes[category.name] = {
                    screen: StackNavigator({
                        'isim': {
                            screen: ProductPage
                        }
                    },{
                        headerMode : 'none',
                        initialRouteParams : {
                            categoryName : category.name,
                            products : category.products
                        }
                    })
                }
            }
            
        } else {

            console.log("This category has no products !");

        }

    })

    console.log("OHA : " + JSON.stringify(routes));

    const ShopCatTabNav = TabNavigator(routes, {
        tabBarPosition: 'top',
        tabBarComponent: props => <TabMenuItems props={props} />
    })

    return <ShopCatTabNav />

}

As last , I will show you customized tab navigation bar I built :

最后,我将向您展示我构建的自定义选项卡导航栏:

const TabMenuItems = ({props}) => {

    const { activeTintColor, tab, tabbar, tabText, inactiveTintColor } = styles;
    const { index } = props.navigation.state;
    return(
        <View>
        <ScrollView contentContainerStyle={{ flex : 1 }} horizontal showsHorizontalScrollIndicator={false} style={{backgroundColor : '#FFAEB9'}}>
        {
            props.navigation.state.routes.length ? (
                props.navigation.state.routes.map((route,number)=>{
                    const focused = ( index === number ) ? '#1874CD' : '#FF6A6A';
                    const tintColor = focused ? activeTintColor : inactiveTintColor;
                    return (
                        <TouchableWithoutFeedback
                            key={route.key}
                            onPress={() => {
                                props.jumpToIndex(number)
                            }}
                            delayPressIn={0}
                            >
                            <View style={{marginLeft : 20, marginTop : height / 40, shadowOpacity : 25, alignSelf : 'flex-start' }}>
                                <Text style={{borderRadius : 5, fontWeight : 'bold', borderWidth :2, paddingTop : 5,color : 'white', height : height/18, width : width/5,textAlign : 'center', backgroundColor : focused, borderStyle: 'dashed',borderColor : '#CD2626'}}>
                                {props.getLabel({route, number})}
                                </Text>
                            </View>
                        </TouchableWithoutFeedback>
                    )
                })
            ) : null
        }
    </ScrollView>
    </View>
    )
}

export default TabMenuItems;

#1


3  

Your TabStack file:

您的TabStack文件:

const CATEGORIES = {
  "Food": { screen: FoodStack },
  // ...
}

export default (screenNames) => {
  const screens = screenNames.reduce((total, name) => ({...total, [name]: CATEGORIES[name]}), {})
  const TabStack = TabNavigator(screens,
    {
        tabBarComponent : props => <CustomTabItems props={props}/>,
        tabBarPosition: 'top',
        animationEnabled : true,
        initialRouteName : 'Food',
        swipeEnabled: true,
        tabBarOptions : {

            scrollEnabled : true
        }
  })
  return TabStack
}

Your Root file:

你的根文件:

import getTabStack from './TabStack'


class Root extends Component {

    state = {
      categoriesNames: null
    }

    componentWillMount() {
      // assuming result is ["Food", "Drink", ... ]
      Api.fetchCategories().then((result) => {
        this.setState({ categoriesNames: result })
      })
    }

    render() {
      const { categoriesNames } = this.state
      if (!categoriesNames) {
        return <SplashScreen />
      }
      const TabStack = getTabStack(categoriesNames)
      return (
        <Provider store={store} >
          <TabStack />
        </Provider>
      );
    }
  }

#2


1  

Here I would like to post a method for creating tab bar according to the data we fetched from some API etc programmatically.

在这里,我想根据我们从一些API等以编程方式提取的数据发布一个创建标签栏的方法。

Here we fetch the data from API in this example, this code from the top level component :

这里我们从这个例子中的API中获取数据,这个代码来自*组件:

renderShopTab() {
        const { client } = this.props;
        try {
            const { categories } = client.readQuery({
                query: gql`
                {
                    categories{
                        id
                        name
                        products{
                            id
                            name
                            price
                            quantity
                        }
                    }
                }`
            })

            console.log("Categories  :" + categories);

            return (
                <ShopCreator categories={categories} />
            )

        } catch (error) {
            console.log("Error occured creating the categories due to the : " + error);

            return (
                <View>
                    <Text>
                        Loading...
                    </Text>
                </View>
            )

        }

    }

This code snippet is from creator of the tab bar dynamically :

此代码段动态地来自标签栏的创建者:

export const ShopCreator = ({ categories }) => {

    // This script will create a TabNavigator for categories and StackNavigators for each member of the categories !

    let categoryStack = {};

    let routes = {};


    categories.forEach((category) => {

        if (category.products.length > 0) {

            const { catname } = category.name;

            if (category.name != undefined) {

                routes[category.name] = {
                    screen: StackNavigator({
                        'isim': {
                            screen: ProductPage
                        }
                    },{
                        headerMode : 'none',
                        initialRouteParams : {
                            categoryName : category.name,
                            products : category.products
                        }
                    })
                }
            }
            
        } else {

            console.log("This category has no products !");

        }

    })

    console.log("OHA : " + JSON.stringify(routes));

    const ShopCatTabNav = TabNavigator(routes, {
        tabBarPosition: 'top',
        tabBarComponent: props => <TabMenuItems props={props} />
    })

    return <ShopCatTabNav />

}

As last , I will show you customized tab navigation bar I built :

最后,我将向您展示我构建的自定义选项卡导航栏:

const TabMenuItems = ({props}) => {

    const { activeTintColor, tab, tabbar, tabText, inactiveTintColor } = styles;
    const { index } = props.navigation.state;
    return(
        <View>
        <ScrollView contentContainerStyle={{ flex : 1 }} horizontal showsHorizontalScrollIndicator={false} style={{backgroundColor : '#FFAEB9'}}>
        {
            props.navigation.state.routes.length ? (
                props.navigation.state.routes.map((route,number)=>{
                    const focused = ( index === number ) ? '#1874CD' : '#FF6A6A';
                    const tintColor = focused ? activeTintColor : inactiveTintColor;
                    return (
                        <TouchableWithoutFeedback
                            key={route.key}
                            onPress={() => {
                                props.jumpToIndex(number)
                            }}
                            delayPressIn={0}
                            >
                            <View style={{marginLeft : 20, marginTop : height / 40, shadowOpacity : 25, alignSelf : 'flex-start' }}>
                                <Text style={{borderRadius : 5, fontWeight : 'bold', borderWidth :2, paddingTop : 5,color : 'white', height : height/18, width : width/5,textAlign : 'center', backgroundColor : focused, borderStyle: 'dashed',borderColor : '#CD2626'}}>
                                {props.getLabel({route, number})}
                                </Text>
                            </View>
                        </TouchableWithoutFeedback>
                    )
                })
            ) : null
        }
    </ScrollView>
    </View>
    )
}

export default TabMenuItems;