react-native의 중첩 서랍 메뉴

중첩된 다단계 서랍 메뉴로 반응 네이티브 앱을 만듭니다.
이 문서에서는 react-navigation을 사용하여 서랍 메뉴를 관리합니다.

여기에 있는 전체 앱 빌드의 코드는 https://github.com/Merlier/rn_example_menu_drawer_nested.git에서 사용할 수 있습니다.

참고: 여기서 app.js 표시는 단순화되었습니다. 깔끔하게 코딩하려면 모든 탐색 및 화면 기능을 분리된 jsx 파일로 내보내야 합니다.

시작하다



요구 사항:
  • 반응 네이티브 >= 0.60

  • 먼저 새 react-native 프로젝트를 시작하십시오.

    $ npx react-native init rn_example_menu_drawer_nested
    


    react-navigation 모듈을 설치합니다:

    $ npm install --save @react-navigation/native
    



    $ npm install --save react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
    


    app.js를 수정하여 두 개의 화면 홈 및 설정을 추가합니다.

    /**
     * Sample React Native App
     * https://github.com/facebook/react-native
     *
     * @format
     * @flow strict-local
     */
    
    import 'react-native-gesture-handler';
    import React from 'react';
    import {StyleSheet, View, Text} from 'react-native';
    import {NavigationContainer} from '@react-navigation/native';
    import {createStackNavigator} from '@react-navigation/stack';
    
    function HomeScreen() {
      return (
        <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
          <Text>Home Screen</Text>
        </View>
      );
    }
    
    function SettingsScreen() {
      return (
        <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
          <Text>Settings Screen</Text>
        </View>
      );
    }
    
    const Stack = createStackNavigator();
    
    const App: () => React$Node = () => {
      return (
        <NavigationContainer>
          <Stack.Navigator>
            <Stack.Screen name="Home" component={HomeScreen} />
            <Stack.Screen name="Settings" component={SettingsScreen} />
          </Stack.Navigator>
        </NavigationContainer>
      );
    };
    
    const styles = StyleSheet.create({});
    
    export default App;
    
    




    그냥 심플한 서랍



    이제 간단한 서랍 메뉴를 만들 것입니다.
    먼저 해당 서랍 모듈을 설치합니다.

    $ npm install --save @react-navigation/drawer
    


    모든 드로어 경로 정의를 포함할 drawerItemsMain.js를 추가합니다.

    export const drawerItemsMain = [
      {
        key: 'Home',
        title: 'Home',
        route: {nav: 'MainDrawer', routeName: 'Home', title: 'Home'},
      },
      {
        key: 'Settings',
        title: 'Settings',
        route: {nav: 'MainDrawer', routeName: 'Settings', title: 'Settings'},
      },
    ];
    
    


    CustomDrawerContent.js를 생성하여 사용자 지정 서랍을 렌더링합니다.

    import React from 'react';
    import {
      StyleSheet,
      ScrollView,
      View,
      Text,
      TouchableOpacity,
      SafeAreaView,
      Image,
    } from 'react-native';
    
    function CustomDrawerContent(props) {
      const onItemPress = (key) => {
        const filteredMainDrawerRoutes = props.drawerItems.find((e) => {
          return e.key === key;
        });
        const selectedRoute = filteredMainDrawerRoutes.route;
        props.navigation.toggleDrawer();
        props.navigation.navigate(selectedRoute.nav, {
          screen: selectedRoute.routeName,
        });
      };
    
      const logOut = async () => console.log('log out');
    
      function renderMainDrawer() {
        return (
          <View>
            {props.drawerItems.map((parent) => (
              <View key={parent.key}>
                <TouchableOpacity
                  key={parent.key}
                  testID={parent.key}
                  onPress={() => {
                    onItemPress(parent.key);
                  }}>
                  <View style={styles.parentItem}>
                    <Text style={[styles.icon, styles.title]}>{parent.title}</Text>
                  </View>
                </TouchableOpacity>
              </View>
            ))}
            {renderLogoutBtn()}
          </View>
        );
      }
    
      function renderLogoutBtn() {
        return (
          <View>
            <TouchableOpacity onPress={logOut} testID="customDrawer-logout">
              <View style={styles.parentItem}>
                <Text style={styles.title}>{'Log out'}</Text>
              </View>
            </TouchableOpacity>
          </View>
        );
      }
    
      return (
        <ScrollView style={styles.drawerContainer}>
          <SafeAreaView
            style={styles.container}
            forceInset={{top: 'always', horizontal: 'never'}}>
            <View style={styles.centered}>
              <Image
                source={{uri: 'https://reactjs.org/logo-og.png'}}
                style={styles.logo}
              />
            </View>
            {renderMainDrawer()}
          </SafeAreaView>
        </ScrollView>
      );
    }
    
    const styles = StyleSheet.create({
      headerContainer: {
        height: 100,
        flexDirection: 'row',
        paddingVertical: 28,
        justifyContent: 'center',
        alignItems: 'center',
      },
      logo: {
        width: 100,
        height: 75,
      },
      drawerContainer: {
        backgroundColor: '#222222',
      },
      container: {
        flex: 1,
        zIndex: 1000,
      },
      centered: {
        alignItems: 'center',
      },
      parentItem: {
        flexDirection: 'row',
        alignItems: 'center',
        borderBottomWidth: 1,
        borderBottomColor: '#F0F0F0',
        paddingTop: 4,
        paddingBottom: 4,
      },
      title: {
        margin: 16,
        fontWeight: 'bold',
        color: '#F0F0F0',
        textAlign: 'center',
      },
    });
    
    export default CustomDrawerContent;
    
    


    그런 다음 CustomHeader.js를 추가하여 사용자 지정 헤더를 만듭니다.

    import React from 'react';
    import {
      StyleSheet,
      TouchableOpacity,
      View,
      Text,
      SafeAreaView,
    } from 'react-native';
    import {DrawerActions} from '@react-navigation/native';
    
    function CustomHeader(props) {
      const toggleDrawer = () =>
        props.navigation.dispatch(DrawerActions.toggleDrawer());
    
      return (
        <SafeAreaView>
          <View style={styles.headerContainer}>
            <View style={styles.headerLeft}>
              <TouchableOpacity
                onPress={toggleDrawer}
                style={styles.leftButton}
                testID="CustomHeader-toggleDrawer">
                <Text style={styles.buttonTxt}>MENU</Text>
              </TouchableOpacity>
            </View>
            <View style={styles.header}>
              <Text style={styles.headerTxt}>HEADER</Text>
            </View>
          </View>
        </SafeAreaView>
      );
    }
    
    const styles = StyleSheet.create({
      headerContainer: {
        flexDirection: 'row',
        justifyContent: 'space-around',
        alignItems: 'center',
        backgroundColor: '#222222',
        minHeight: 40,
      },
      headerLeft: {
        flexDirection: 'row',
      },
      leftButton: {
        marginLeft: 10,
      },
      header: {
        flex: 1,
        flexDirection: 'row',
        justifyContent: 'center',
        paddingRight: 40,
      },
      buttonTxt: {
        color: '#ddd',
        fontWeight: 'bold',
      },
      headerTxt: {
        color: '#ddd',
      },
    });
    
    export default CustomHeader;
    
    


    다음과 같이 app.js를 수정합니다.

    
    ...
    
    import {createDrawerNavigator} from '@react-navigation/drawer';
    
    import {drawerItemsMain} from './drawerItemsMain';
    import CustomDrawerContent from './CustomDrawerContent.js';
    import CustomHeader from './CustomHeader';
    
    const Drawer = createDrawerNavigator();
    
    ...
    
    function MainDrawerNavigation() {
      return (
        <Drawer.Navigator
          initialRouteName="Home"
          drawerContent={(props) => (
            <CustomDrawerContent drawerItems={drawerItemsMain} {...props} />
          )}>
          <Drawer.Screen name="Home" component={HomeScreen} />
          <Drawer.Screen name="Settings" component={SettingsScreen} />
        </Drawer.Navigator>
      );
    }
    
    const App: () => React$Node = () => {
      return (
        <NavigationContainer>
          <Stack.Navigator
            screenOptions={{
              headerMode: 'screen',
              headerTintColor: '#404554',
              headerTitleStyle: {
                fontWeight: 'bold',
              },
              header: (props) => {
                return <CustomHeader {...props} />;
              },
            }}>
            <Stack.Screen name="MainDrawer" component={MainDrawerNavigation} />
          </Stack.Navigator>
        </NavigationContainer>
      );
    };
    
    ...
    
    




    마지막으로 중첩된 다단계 서랍



    다음과 같이 draweritemsMain.js를 수정합니다.

    export const drawerItemsMain = [
      {
        key: 'Home',
        title: 'Home',
        routes: [{nav: 'MainDrawer', routeName: 'Home', title: 'Home'}],
      },
      {
        key: 'Settings',
        title: 'Settings',
        routes: [
          {nav: 'MainDrawer', routeName: 'Settings1', title: 'Settings 1'},
          {nav: 'MainDrawer', routeName: 'Settings2', title: 'Settings 2'},
        ],
      },
    ];
    
    


    CustomDrawerContent.js는 다음과 같습니다.

    import React, {useState, useRef, useEffect} from 'react';
    import {
      StyleSheet,
      ScrollView,
      View,
      Text,
      TouchableOpacity,
      SafeAreaView,
      Image,
    } from 'react-native';
    
    function CustomDrawerContent(props) {
      const [mainDrawer, setMainDrawer] = useState(true);
      const [filteredItems, setFilteredItems] = useState([]);
    
      const toggleMainDrawer = () => {
        setMainDrawer(true);
        setFilteredItems([]);
      };
    
      const onItemParentPress = (key) => {
        const filteredMainDrawerRoutes = props.drawerItems.find((e) => {
          return e.key === key;
        });
        if (filteredMainDrawerRoutes.routes.length === 1) {
          const selectedRoute = filteredMainDrawerRoutes.routes[0];
          props.navigation.toggleDrawer();
          props.navigation.navigate(selectedRoute.nav, {
            screen: selectedRoute.routeName,
          });
        } else {
          setMainDrawer(false);
          setFilteredItems(filteredMainDrawerRoutes);
        }
      };
    
      const logOut = async () => console.log('log out');
    
      function renderMainDrawer() {
        return (
          <View>
            {props.drawerItems.map((parent) => (
              <View key={parent.key}>
                <TouchableOpacity
                  key={parent.key}
                  testID={parent.key}
                  onPress={() => {
                    onItemParentPress(parent.key);
                  }}>
                  <View style={styles.parentItem}>
                    <Text style={[styles.icon, styles.title]}>{parent.title}</Text>
                  </View>
                </TouchableOpacity>
              </View>
            ))}
            {renderLogoutBtn()}
          </View>
        );
      }
    
      function renderFilteredItemsDrawer() {
        return (
          <View>
            <TouchableOpacity
              onPress={() => toggleMainDrawer()}
              style={styles.backButtonRow}>
              <Text style={[styles.backButtonText, styles.title]}>{'BACK'}</Text>
            </TouchableOpacity>
            {filteredItems.routes.map((route) => {
              return (
                <TouchableOpacity
                  key={route.routeName}
                  testID={route.routeName}
                  onPress={() =>
                    props.navigation.navigate(route.nav, {
                      screen: route.routeName,
                    })
                  }
                  style={styles.item}>
                  <Text style={styles.title}>{route.title}</Text>
                </TouchableOpacity>
              );
            })}
          </View>
        );
      }
    
      function renderLogoutBtn() {
        return (
          <View>
            <TouchableOpacity onPress={logOut} testID="customDrawer-logout">
              <View style={styles.parentItem}>
                <Text style={styles.title}>{'Log out'}</Text>
              </View>
            </TouchableOpacity>
          </View>
        );
      }
    
      return (
        <ScrollView style={styles.drawerContainer}>
          <SafeAreaView
            style={styles.container}
            forceInset={{top: 'always', horizontal: 'never'}}>
            <View style={styles.centered}>
              <Image
                source={{uri: 'https://reactjs.org/logo-og.png'}}
                style={styles.logo}
              />
            </View>
            {mainDrawer ? renderMainDrawer() : renderFilteredItemsDrawer()}
          </SafeAreaView>
        </ScrollView>
      );
    }
    
    const styles = StyleSheet.create({
      headerContainer: {
        height: 100,
        flexDirection: 'row',
        paddingVertical: 28,
        justifyContent: 'center',
        alignItems: 'center',
      },
      logo: {
        width: 100,
        height: 75,
      },
      drawerContainer: {
        backgroundColor: '#222222',
      },
      container: {
        flex: 1,
        zIndex: 1000,
      },
      centered: {
        alignItems: 'center',
      },
      parentItem: {
        flexDirection: 'row',
        alignItems: 'center',
        borderBottomWidth: 1,
        borderBottomColor: '#F0F0F0',
        paddingTop: 4,
        paddingBottom: 4,
      },
      title: {
        margin: 16,
        fontWeight: 'bold',
        color: '#F0F0F0',
        textAlign: 'center',
      },
      backButtonRow: {
        flexDirection: 'row',
        alignItems: 'center',
        paddingBottom: 17,
        paddingLeft: 3,
        borderBottomColor: '#F0F0F0',
        borderBottomWidth: 1,
      },
      backButtonText: {
        marginLeft: 10,
        color: '#F0F0F0',
      },
    });
    
    export default CustomDrawerContent;
    
    


    그런 다음 app.js에 두 가지 다른 설정 화면을 추가합니다.

    ...
    
    function Settings1Screen() {
      return (
        <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
          <Text>Settings 1 Screen</Text>
        </View>
      );
    }
    
    function Settings2Screen() {
      return (
        <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
          <Text>Settings 2 Screen</Text>
        </View>
      );
    }
    
    function MainDrawerNavigation() {
      return (
        <Drawer.Navigator
          initialRouteName="Home"
          drawerContent={(props) => (
            <CustomDrawerContent drawerItems={drawerItemsMain} {...props} />
          )}>
          <Drawer.Screen name="Home" component={HomeScreen} />
          <Drawer.Screen name="Settings1" component={Settings1Screen} />
          <Drawer.Screen name="Settings2" component={Settings2Screen} />
        </Drawer.Navigator>
      );
    }
    



    $ npx react-native run-android
    


    이제 react-native 앱에 완전히 중첩된 다중 레벨 서랍이 있습니다! :)



    그것이 당신을 도울 수 있기를 바랍니다!
    재미있게 보내세요 :)

    좋은 웹페이지 즐겨찾기