오티스의개발일기

[REACT NATIVE] 인스타그램 클론 코딩 (14) gorhom/bottom-sheet 를 활용한 바텀 시트 만들기 본문

개발/react-native

[REACT NATIVE] 인스타그램 클론 코딩 (14) gorhom/bottom-sheet 를 활용한 바텀 시트 만들기

안되면 될때까지.. 2023. 1. 1. 23:26
728x90


< 이전글

2023.01.01 - [개발/react-native] - [REACT NATIVE] 인스타그램 클론 코딩 (13) 마이페이지 ui 만들기

 

[REACT NATIVE] 인스타그램 클론 코딩 (13) 마이페이지 ui 만들기

< 이전글 2023.01.01 - [개발/react-native] - [REACT NATIVE] 인스타그램 클론 코딩 (12) AsyncStorage 를 사용하여 jwt 토큰 저장하기 [REACT NATIVE] 인스타그램 클론 코딩 (12) AsyncStorage 를 사용하여 jwt 토큰 저장하기

otis.tistory.com



다음글 >

 

2023.01.02 - [개발/react-native] - [REACT NATIVE] 인스타그램 클론 코딩 (15) 로그아웃 구현하기

 

[REACT NATIVE] 인스타그램 클론 코딩 (15) 로그아웃 구현하기

< 이전글 2023.01.01 - [개발/react-native] - [REACT NATIVE] 인스타그램 클론 코딩 (14) gorhom/bottom-sheet 를 활용한 바텀 시트 만들기 [REACT NATIVE] 인스타그램 클론 코딩 (14) gorhom/bottom-sheet 를 활용한 바텀 시트

otis.tistory.com


 

 

 

오늘은 gorhom/bottom-sheet 를 활용하여 바텀 시트를 만들어볼것이다

원래는 로그아웃까지 구현하는 포스팅을 올리려했는데

너무 길어질것같아 오늘은 ui만 제작 해보려고한다.

 

이거 지금 두번쓰는 중이다....

티스토리 이놈.... 임시저장했는데 없어져버렸다......

화가나지만 마음을 가다듬고 시작해보겠다..

 

 


# 오늘 작업할 파일목록

  • App.js
  • babel.config.js
  • BottomNavigation.js
  • myPageBottomSheet.js
  • Header.js(/src/screens/myPage/Header.js)

# 0. 폴더 구조



 

일단 라이브러리를 다운로드 받아보도록 하겠다.

터미널을 열고 명령어를 작성해보자.

yarn add @gorhom/bottom-sheet@^4
yarn add react-native-reanimated react-native-gesture-handler

 

 

라이브러리를 다받았으면 설정해야하는 파일이 몇개가있다.

 

첫번째는 babel.config.js 파일이다.

module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: ['react-native-reanimated/plugin'], // <---- 추가한것
};

주석으로 적어놨으니 확인하기바란다.

reanimated 라이브러리를 사용하려면 플러그인에 등록해야하는 하지않으면 오류가 뜰것이다.

 

두번째는 react-native-gesture-hanlder 모듈이다.

이게 이번에 바뀐거같다.

 

이 제스처 모듈을 사용하려면 루트 파일 상단에 감싸줘야한다.

이것때문에 한 삼십분은 삽질한거같다 ㅎㅎ

import {Provider} from "react-redux";
import store from "./store";
import { StyleSheet, Text, View } from 'react-native'
import AuthNavigaitor from './src/navigations/AuthNavigaiton'
import {GestureHandlerRootView} from 'react-native-gesture-handler'
import { SafeAreaView } from "react-native";export default function App() {
    return (
        <Provider store={store}>
            <GestureHandlerRootView style={{ flex: 1 }}>
                <AuthNavigaitor/>
            </GestureHandlerRootView>
        </Provider>
    );
}

 

<GestureHandlerRootView><GestureHandlerRootView>

이 태그를 꼭 감싸주도록 하자.

 

 

시작하기전 참고사항이있다.

본인은 바텀시트가 바텀네비게이션을 가려야하기때문에

BottomNavigation.js 에 작업을 하였다.

 

이제 본격적으로 시작해보도록 하겠다.

 

 

# 1. BottomNavigation.js

 

import {createBottomTabNavigator} from '@react-navigation/bottom-tabs'
import React, { useCallback, useMemo, useRef } from 'react';

import {
  BottomSheetModal,
  BottomSheetModalProvider,
} from '@gorhom/bottom-sheet';

import {ROUTES} from '../constants/routes';
import HomeScreen from '../screens/HomeScreen';
import CartScreen from '../screens/CartScreen';
import MyPageScreen from '../screens/MyPageScreen';
import PlayScreen from '../screens/PlayScreen';
import SearchScreen from '../screens/SearchScreen';
import styled from 'styled-components/native'
import Ionicons from "react-native-vector-icons/Ionicons";
import {BOTTOM_ICONS} from '../constants/icons';
import { useSelector, useDispatch } from 'react-redux';
import bottomSheetSlicer from '../slicers/bottomSheetSlicer'
import MyPageBottomSheet from '../screens/bottomSheet/MyPageBottomSheet'
const Tab = createBottomTabNavigator();

const BottomNavigation = (props) => {


  const { navigation } = props; // 네비게이션

  const dispatch = useDispatch();

  const theme = useSelector((state) => state.themeSlicer.theme);

   // ref
   const bottomSheetModalRef = useRef(null);

   // variables
   const snapPoints = useMemo(() => ['25%', '50%'], []);
 
   // callbacks
   const handlePresentModalPress = useCallback(() => {
      bottomSheetModalRef.current?.present();
   }, []);

   const handleSheetChanges = useCallback((index) => {
     console.log('handleSheetChanges', index);
     if (index === -1) {
      dispatch(bottomSheetSlicer.actions.updateSlicer({name : 'myPage', active: false}))
     }
   }, []);

  const bottomSheet = useSelector((state) => state.bottomSheetSlicer);

  if (bottomSheet.active) {
    switch(bottomSheet.name) {
      case 'myPage' :
        handlePresentModalPress();
        break;
    }
  }
  

  return ( 
    <>
    <Tab.Navigator initialRouteName={ROUTES.HOME} 
    screenOptions={({route}) => ({
      headerShown: false,
      tabBarShowLabel: false,
      tabBarStyle: {
        borderTopWidth: 1,
        borderTopColor: '#61616236',
        height: 90,    
        padding: 10,
        backgroundColor: theme.backgroundColor,
    },
      tabBarIcon: ({color, size, focused}) => {
        let iconName;
        if (route.name === ROUTES.HOME) {
          iconName = focused ? BOTTOM_ICONS.HOME.activeIcon : BOTTOM_ICONS.HOME.inActiveIcon
        } else if (route.name === ROUTES.SEARCH) {
          iconName = focused ? BOTTOM_ICONS.SEARCH.activeIcon : BOTTOM_ICONS.SEARCH.inActiveIcon
        } else if (route.name === ROUTES.PLAY) {
          iconName = focused ? BOTTOM_ICONS.PLAY.activeIcon : BOTTOM_ICONS.PLAY.inActiveIcon
        } else if (route.name === ROUTES.CART) {
          iconName = focused ? BOTTOM_ICONS.CART.activeIcon : BOTTOM_ICONS.CART.inActiveIcon
        } else if (route.name === ROUTES.MYPAGE) {
          return <Avatar source={{uri: 'https://i.ibb.co/ZhB1QPv/image.jpg'}}/>
        }

        return <Ionicons name={iconName} size={28} color={theme.TextColor}/>
      }
    })}>
      <Tab.Screen name={ROUTES.HOME} component={HomeScreen} ></Tab.Screen>
      <Tab.Screen name={ROUTES.SEARCH} component={SearchScreen}></Tab.Screen>
      <Tab.Screen name={ROUTES.PLAY} component={PlayScreen}></Tab.Screen>
      <Tab.Screen name={ROUTES.CART} component={CartScreen}></Tab.Screen>
      <Tab.Screen name={ROUTES.MYPAGE} component={MyPageScreen}></Tab.Screen>
    </Tab.Navigator>
    <BottomSheetModalProvider>
      <View>
        <BottomSheetModal
          ref={bottomSheetModalRef}
          index={1}
          snapPoints={snapPoints}
          onChange={handleSheetChanges}
        >
          <View>
            <MyPageBottomSheet navigation={navigation}/>
          </View>
        </BottomSheetModal>
      </View>
    </BottomSheetModalProvider>
    </>
  )
}

const Avatar = styled.Image`
    width: 32px;
    height: 32px;
    border-radius: 50px;
    border-width: 2px;
    border-color: white;
`;


const View = styled.View``

const Text = styled.Text``

const Button = styled.Button``

export default BottomNavigation

 

 

기본적으로 어떤 바텀 네비게이션이 추가될지몰라

BottomNavigation 에 중점을 두고 myPageBottomSheet.js 라는 파일을 따로 빼두었다.

 

# 2.myPageBottomSheet.js

import styled from "styled-components/native";
import React from 'react'
import {Divider} from 'react-native-elements'
import Ionicons from "react-native-vector-icons/Ionicons";
import {ICONS} from '../../constants/icons'
import {Alert} from 'react-native'
import { useDispatch, useSelector } from "react-redux"; // userDispatch = 데이터 변경시 사용 // useSelector = 데이터 가져올때 사용
import {logOutRequest} from '../../actions/userAction'
import * as $Util from '../../constants/utils'
const myPageBottomSheet = ({navigation}) => {
    const dispatch = useDispatch();

    const openLogOutAlert = () => {
        return (
            Alert.alert(
                '로그아웃 하시겠습니까?',
                '로그아웃',
                [
                    {
                        text: '로그아웃',
                        onPress: () => {dispatch(logOutRequest(navigation))}
                    },
                    {
                        text: '취소',
                        style: "cancel"
                    },
                ],
                { cancelable: false }
            )
        )
    }

  return (
    <Container>
        <TouchableOpacity onPress={openLogOutAlert}>
            <Box>
                <TextBox>
                    <Ionicons name="log-out-outline" size={28} color="black" />
                    <Text>로그아웃</Text>
                </TextBox>
                <Divider width={1}/>
            </Box>
        </TouchableOpacity>
    </Container>
  )
}

export default myPageBottomSheet

const Container = styled.View`
    padding: 10px 15px;
`
const Box = styled.View``

const TextBox = styled.View`
    flex-direction: row;
    align-items: center;
    margin-bottom: 10px;
`

const Text = styled.Text`
    margin-left: 10px;
`

const TouchableOpacity = styled.TouchableOpacity`

`;

 

 

 

 

자이제 동적하는지 확인해보겠다.

 

 

 

정상적으로 동작하는걸 확인할수있다.

 

이것으로 포스팅을 마치도록 하겠다.

 

다음 포스팅에는 진짜로 로그아웃을 구현할것이니 참고하기 바란다.

 

 

 

 

# 깃허브 주소


https://github.com/1domybest/react-native-ig-clone.git

 

GitHub - 1domybest/react-native-ig-clone

Contribute to 1domybest/react-native-ig-clone development by creating an account on GitHub.

github.com

 


다음글 >

2023.01.02 - [개발/react-native] - [REACT NATIVE] 인스타그램 클론 코딩 (15) 로그아웃 구현하기

 

[REACT NATIVE] 인스타그램 클론 코딩 (15) 로그아웃 구현하기

< 이전글 2023.01.01 - [개발/react-native] - [REACT NATIVE] 인스타그램 클론 코딩 (14) gorhom/bottom-sheet 를 활용한 바텀 시트 만들기 [REACT NATIVE] 인스타그램 클론 코딩 (14) gorhom/bottom-sheet 를 활용한 바텀 시트

otis.tistory.com


 

728x90
Comments