Intro
ํ๋ก์ ํธ ๋ก์ง์์ ์ปค๋ฎค๋ํฐ ๊ฒ์๊ธ์์ ์์ฑ์ ํ๋กํ์ ๋๋ฅด๊ฑฐ๋ ๋ฒ๊ฐ ๋ฏธํ ์์ 1:1 ์ฑํ ์ ๋๋ฅผ ๋ ์ฌ์ฉ์์ ํ๋กํ์ ์กฐํํ ์ ์๋ ๋ชจ๋ฌ์ฐฝ์ ๋ณด์ฌ์ฃผ๋๋ก ๊ธฐํํ์๋ค. ํ๋กํ ๋ชจ๋ฌ์ ๊ฒฝ์ฐ ์ฌ๋ฌ ํ์ด์ง์์ ๋ชจ๋ฌ ์ปดํฌ๋ํธ๋ฅผ ํ์ํ ๋๋ง๋ค ๊ตฌํํด์ผํ๋ค. ์ด๋ฌํ ์์ ์ ํ ํ์ผ ๋ด์ <Modal /> ์ปดํฌ๋ํธ๋ฅผ ์ถ๊ฐํด ๊ตฌํ์ ํ๋ฉด ํ ํ์ด์ง์ ์ฝ๋๊ฐ ๊ธธ์ด์ง๊ฒ ๋๊ณ ๊ฐ๋ ์ฑ์ด ๋จ์ด์ก๋ค.
๋ชจ๋ฌ๋ง ๋ฐ๋ก ํ์ผ๋ก ๋ถ๋ฆฌํด์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ผ๋ก๋ ์ฝ๋๊ฐ ๊ธธ์ด์ง๋ ๊ฒ์ ๋ฐฉ์งํ ์ ์์ง๋ง, ๋ชจ๋ฌ์ ๊ตฌํํ ๋๋ง๋ค ์ด์ ์ ๊ตฌํํ๋ ๋ชจ๋ฌ ์ปดํฌ๋ํธ๋ฅผ ์ฐธ๊ณ ํ๋ฉด์ ๋ชจ๋ฌ์ ์์ฑ์ด๋ ์คํ์ผ์ ๋ณต์ฌ/๋ถ์ฌ๋ฃ์ผ๋ฉฐ ์ฝ๋๋ฅผ ์ง๊ณ ์๋ ๊ฒ ๋นํจ์จ์ ์ผ๋ก ๋๊ปด์ก๋ค.
๋ซ๊ธฐ๋ ๋ชจ๋ฌ ์ธ์ ์ฐฝ์ ๋๋ ์ ๋ ๋ชจ๋ฌ์ด ๋ซํ๋ ๊ธฐ๋ฅ๋ ๋์ผํ๊ธฐ ๋๋ฌธ์ ์ด๊ฑธ ๊ณตํต ์ปดํฌ๋ํธ๋ก ์ฌ์ฉํ ์ ์์ง ์์๊น ๊ณ ๋ฏผํ๋ค๊ฐ.. ํ์ ์ค ํ๋ถ์ด ์ด์ ๋น์ทํ ๋ด์ฉ์ ๋ธ๋ก๊ทธ ๊ธ์ ๊ณต์ ํด์ฃผ์ ์ ์ฐธ๊ณ ํด์ ๋ง๋ค์ด๋ณด์๋ค.
Context API๋ก ๋ชจ๋ฌ ๊ด๋ฆฌํ๊ธฐ
๋ชจ๋ฌ์ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ์ปจํ ์คํธ์ ๋ชจ๋ฌ์ ์ด๊ณ ๋ซ๋ ํจ์ ์ปจํ ์คํธ๋ฅผ ๋ง๋ค๊ณ Provider๋ก ์ ๊ณตํ๋ค.
- ModalContext.jsx
import React, { createContext, useContext, useState } from 'react';
const ModalStateContext = createContext();
const ModalDispatchContext = createContext();
export const ModalProvider = ({ children }) => {
const [modal, setModal] = useState({ type: null, props: null, onBackdropPress: null });
const openModal = modal => {
setModal(modal);
};
const closeModal = () => {
setModal({ type: null, props: null, onBackdropPress: null });
};
return (
<ModalDispatchContext.Provider value={{ openModal, closeModal }}>
<ModalStateContext.Provider value={modal}>{children}</ModalStateContext.Provider>
</ModalDispatchContext.Provider>
);
};
export function useModalState() {
return useContext(ModalStateContext);
}
export function useModalDispatch() {
return useContext(ModalDispatchContext);
}
- ModalStateContext: Modal์ type๊ณผ props๋ฅผ ๊ฐ์ง ๊ฐ์ฒด๋ฅผ ๊ฐ๋๋ค. (ModalContainer.jsx ์ฐธ๊ณ )
- ModalDispatchContext: Modal์ ์ด๊ณ (openModal) ๋ซ๋ (closeModal) ๊ธฐ๋ฅ์ ์ ์ญ์ ์ผ๋ก ์ฌ์ฉํ๊ธฐ ์ํ ์ปจํ ์คํธ
์ปจํ ์คํธ๋ ๊ฐ์ด ๋ณ๊ฒฝ๋๋ฉด ํด๋น ์ปจํ ์คํธ๋ฅผ ์ฌ์ฉํ๋ ๋ชจ๋ ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง๋๋ค. ์ํ ๊ฐ์ด ๋ณ๊ฒฝ๋๋ ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ํจ์๋ฅผ ๋ฆฌ๋ ๋๋งํ ํ์๊ฐ ์์ผ๋ฏ๋ก ๋ถ๋ฆฌํ์๋ค.
๋ชจ๋ฌ์ 'react-native-modal' ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ๊ตฌํํ๊ณ ์์๋๋ฐ
https://github.com/react-native-modal/react-native-modal
GitHub - react-native-modal/react-native-modal: An enhanced, animated, customizable Modal for React Native.
An enhanced, animated, customizable Modal for React Native. - react-native-modal/react-native-modal
github.com
ํด๋น Modal ์ปดํฌ๋ํธ์ ์์ฑ๋ค์ด ์ฌ๋ฌ ๋ชจ๋ฌ์ ๊ณตํต์ ์ผ๋ก ์ฌ์ฉ๋๊ณ ์์ด์ ModalContainer ๋ผ๋ ๊ณตํต ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด์ ์์ ์ปดํฌ๋ํธ(children)๋ฅผ ์์ฑ์ผ๋ก ๋ฐ์ ๊ตฌํํด ๋ณด์๋ค.
- ModalContainer.jsx
import React from 'react';
import Modal from 'react-native-modal';
import { useModalDispatch, useModalState } from '../../contexts/ModalContext';
import ProfileModal from './contents/ProfileModal';
import PublicInfoMessage from './contents/PublicInfoMessage';
import SignUpCompleteModal from './contents/SignUpCompleteModal';
import Dropdown from './contents/CitySelectModal';
import AlertModal from './contents/AlertModal';
const MODAL_CONTENTS = {
signUpComplete: { component: SignUpCompleteModal },
userProfile: { component: ProfileModal },
locationDropdown: {
component: Dropdown,
animationInTiming: 1,
animationOutTiming: 1,
},
publicInfoMessage: { component: PublicInfoMessage },
messageAlert: {
component: AlertModal,
animationInTiming: 1,
animationOutTiming: 1,
backdropOpacity: 0,
},
};
const ModalContainer = () => {
const { type, props, onBackdropPress } = useModalState();
const { closeModal } = useModalDispatch();
if (!type) return null;
const {
component: Children,
backdropOpacity = 0.3,
animationInTiming = 300,
animationOutTiming = 300,
disableBackdropPress = false,
} = MODAL_CONTENTS[type];
return (
<Modal
isVisible={true}
backdropOpacity={backdropOpacity}
animationInTiming={animationInTiming}
animationOutTiming={animationOutTiming}
onBackdropPress={disableBackdropPress ? undefined : onBackdropPress || closeModal}>
<Children {...props} />
</Modal>
);
};
export default ModalContainer;
- MODAL_CONTENTS
- component: ์์ ์ปดํฌ๋ํธ (type ๋ช ์นญ)
- ๊ทธ ์ธ Modal ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ด๋ จ ์์ฑ
- ๋ชจ๋ฌ ์ปจํ ์คํธ์์ ๋ฐ์์จ type, props, onBackdropPress ๊ฐ๋ค๋ก ๋ชจ๋ฌ ์ปดํฌ๋ํธ๋ฅผ ๊ตฌํํ๋ค.
์์ฑํ ์ปจํ ์คํธ์ ModalContainer ์ปดํฌ๋ํธ๋ App์ ๋ฐฐ์นํ๋ค.
- App.jsx
import React from 'react';
import { ModalProvider } from './src/contexts/ModalContext';
import ModalContainer from './src/components/modals/ModalContainer';
function App() {
return (
<ModalProvider>
<MainStack />
<ModalContainer />
</ModalProvider>
);
}
export default App;
- ์ฌ์ฉ ์
const { openModal } = useModalDispatch();
openModal({
type: 'userProfile',
props: { user: { profileImage: authorImg, profileName: authorName } },
})
ํ๋ก์ ํธ์ ๋ชจ๋ฌ ์ปจํ ์คํธ๋ฅผ ๊ตฌํํ๊ณ ๊ฐ๋ฐํ ์ง 3์ฃผ ์ ๋ ๋์๋๋ฐ ๋ชจ๋ฌ์ ๊ตฌํํ ๋ ์ค๋ณต ์ฝ๋๊ฐ ์ค์ด๋ค์๊ณ ๊ฐ ํ์ด์ง์์ ๋งค๋ฒ ๊ตฌํํ๋ ๋ชจ๋ฌ ์ปดํฌ๋ํธ๋ฅผ ๋ถ๋ฆฌํจ์ผ๋ก์จ ๊ฐ๋ ์ฑ๋ ๋์์ก๋ค. ํ์๋ค๋ ๋ชจ๋ ๋ง์กฑํ๋ฉฐ ์ฌ์ฉ์ค์ด๋ค. ๊ณตํต์ ์ผ๋ก ์ฌ์ฉ๋๋ ์ฝ๋๋ค์ ์ค์ฌ๋๊ฐ๋ ๋ฆฌํฉํ ๋ง ๊ณผ์ ์ ๊พธ์คํ ํด์ผ๊ฒ ๋ค.
์ฐธ๊ณ ํ ์๋ฃ)
[React] ํจ์จ์ ์ผ๋ก ๋ชจ๋ฌ ๊ด๋ฆฌํ๊ธฐ
๋ฌด๋ ค 3๊ฐ์ ๋ง์ ๋์์์ต๋๋ค... ์ด ๊ธ์ Portal์ ์ฌ์ฉํ ๋ชจ๋ฌ์ฐฝ ๋ง๋ค๊ธฐ์์ ์ด์ด์ง๋๋ค. ๋ชจ๋ฌ์ ํ์ํ ์ปดํฌ๋ํธ์์ ๊ทธ๋๊ทธ๋ ๋ ๋๋ง ํ๋ ๋ฐฉ๋ฒ์ ๋ถํ์ํ ์ฝ๋๋ฅผ ๋๋ฆฌ๋ฉฐ, ๊ฐ๊ฐ์ ์ปดํฌ๋ํธ์
leego.tistory.com