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

React Native - Creating Navigator dynamically with React Navigation

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

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

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

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
        }
})

谢谢

这里是根代码

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);

您的 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
}

您的根文件:

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>
      );
    }
  }

在这里,我想 post 一种根据我们从一些 API 等地方以编程方式获取的数据创建标签栏的方法。

在此示例中,我们从 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>
            )

        }

    }

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

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 />

}

最后,我将向您展示我构建的自定义标签导航栏:

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;

这个问题已经有一段时间了,但如果还有人在看的话。我宁愿使用 react-native-navigation 而不是这个库。