๐Ÿ“ฑ Cross Platform/React Native

[React Native] ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ Custom Modal ๋งŒ๋“ค๊ธฐ (feat. Reusable Custom Modal)

Fomagran ๐Ÿ’ป 2022. 6. 21. 04:48
728x90
๋ฐ˜์‘ํ˜•

์•ˆ๋…•ํ•˜์„ธ์š” Foma ์ž…๋‹ˆ๋‹ค.

 

์ €๋ฒˆ ๊ธ€์— Modal์— ๋Œ€ํ•ด์„œ ์•Œ์•„ ๋ณด์•˜๋Š”๋ฐ์š”. (์•ˆ๋ณด์‹  ๋ถ„์€ ์—ฌ๊ธฐ ์—์„œ ๋ด์ฃผ์„ธ์š”!)

 

์˜ค๋Š˜์€ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปค์Šคํ…€ Modal์„ ๋งŒ๋“ค์–ด ๋ณด๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ์•„ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

๋ฐ”๋กœ ์‹œ์ž‘ํ• ๊ฒŒ์š”~


Modal.tsx

 

๊ฐ€์žฅ ๋จผ์ € ํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ๋“ค์„ import ํ•ด์ค๋‹ˆ๋‹ค.

 

Modal์„ importํ•  ๋• ์ปค์Šคํ…€ ํ•˜๋Š” Component Modal๊ณผ ์ด๋ฆ„์ด ๊ฒน์น˜๋ฏ€๋กœ as๋ฅผ ์ด์šฉํ•ด์„œ DefaultModal๋กœ ๋ฐ”๊ฟ”์ค๋‹ˆ๋‹ค.

 

import { FunctionComponent, useState } from "react";
import { StyleSheet, View, Modal as DefaultModal, Button } from "react-native";

 

Modal Props

 

๋ชจ๋‹ฌ์˜ ์†์„ฑ๋“ค์„ ์ •ํ•ฉ๋‹ˆ๋‹ค.

 

1. activator

 

Custom Modal ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ํ™”๋ฉด์—์„œ ์ง์ ‘ ๋™์ž‘ํ•  ํ•จ์ˆ˜๋ฅผ ์ปค์Šคํ…€ ํ•˜๋„๋ก ๋„์™€์ฃผ๋Š” ์†์„ฑ์ž…๋‹ˆ๋‹ค.

 

handlOpen ์ต๋ช… ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ Openํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

 

2. children

 

๋ชจ๋‹ฌ ๋ทฐ ์•ˆ์— ๋“ค์–ด๊ฐˆ ํ™”๋ฉด์„ ๋‚˜ํƒ€๋‚ผ ์†์„ฑ์ž…๋‹ˆ๋‹ค.

 

type ModalProps = {
  activator?: FunctionComponent<{ handleOpen: () => void }>;
  children: React.ReactNode;
};

 

Modal

 

์œ„์—์„œ ๋งŒ๋“  ModalProps๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›๋Š” Modal ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ์ค๋‹ˆ๋‹ค.

 

visible ์†์„ฑ์„ ์ƒํƒœ๋กœ ๊ด€๋ฆฌํ•  useState๋ฅผ ๋งŒ๋“ค์–ด ์ค๋‹ˆ๋‹ค.

 

export function Modal({ activator: Activator, children }: ModalProps) {
  const [isVisible, setVisible] = useState(false);
  ...

 

View

 

DefaultModal๋กœ ์†์„ฑ์„ ์›ํ•˜๋Š”๋Œ€๋กœ ์ปค์Šคํ…€ํ•ฉ๋‹ˆ๋‹ค.

 

View ํƒœ๊ทธ๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ๊ณ  children์„ ๋„ฃ์–ด์ค˜ Modal์„ ์‚ฌ์šฉํ•˜๋Š” ํ™”๋ฉด์—์„œ ์ปค์Šคํ…€ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

 

Close ๋ฒ„ํŠผ์„ ๋งŒ๋“ค์–ด์ค˜ ํ•ด๋‹น ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด Modal์ด ๊บผ์ง€๋„๋ก ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

 

  return (
    <View>
      <DefaultModal
        visible={isVisible}
        ...
      >
        <View style={styles.contentView}>
          {children}
          <Button onPress={() => setVisible(false)} title="Close"></Button>
        </View>
      </DefaultModal>
	...
  );

 

Activator๊ฐ€ ์žˆ๋‹ค๋ฉด Modal์„ ๋„์šฐ๋Š” ํ™”๋ฉด์—์„œ handleOpen์ด ์‹คํ–‰ ๋˜์—ˆ๋‹ค๋ฉด ๋ชจ๋‹ฌ ํ™”๋ฉด์ด ๋œฐ ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

 

๋งŒ์•ฝ ์กด์žฌ ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด Open ๋ฒ„ํŠผ์„ ๋งŒ๋“ค์–ด ์ฃผ๊ณ  ๋ˆŒ๋ €์„ ๋•Œ ๋ชจ๋‹ฌ ํ™”๋ฉด์ด ๋œฐ ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

 

      {Activator ? (
        <Activator handleOpen={() => setVisible(true)} />
      ) : (
        <Button onPress={() => setVisible(true)} title="Open"></Button>
      )}
    </View>

Screen.tsx

 

Modal์„ ๋„์šธ ํ™”๋ฉด์—์„œ ์œ„์—์„œ ๋งŒ๋“  Modal์„ import ํ•ด์ค๋‹ˆ๋‹ค.

 

import { Modal } from "../components/styled/Modal";

 

ํ•ด๋‹น ํ™”๋ฉด์—์„œ Modal์„ ๋งŒ๋“ค๊ณ  activator์— ์›ํ•˜๋Š” ๋ฒ„ํŠผ์„ ์ปค์Šคํ…€ ํ•ด์ฃผ๊ณ  ํ•ด๋‹น ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋–„ handleOpen์ด ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

 

Modal ์ปดํฌ๋„ŒํŠธ ํƒœ๊ทธ ์•ˆ์— Text๋ฅผ children์œผ๋กœ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค.

 

export default function Screen({ route }: Navigation) {
  return (
   ...
      <Modal
        activator={({ handleOpen }) => (
          <Button onPress={handleOpen} title="Open"></Button>
        )}
      >
        <Text>Hello This is a activator modal view</Text> //children
      </Modal>
	...

 

activator๊ฐ€ ์—†์ด Modal์„ ๋งŒ๋“ค๊ณ  ์‹ถ๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•ด ์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

      <Modal>
        <Text>Hello This is a modal view</Text>
      </Modal>

์‹คํ–‰ํ™”๋ฉด

 

 


Source Code

 

Modal.tsx

 

import { FunctionComponent, useState } from "react";
import { StyleSheet, View, Modal as DefaultModal, Button } from "react-native";

type ModalProps = {
  activator?: FunctionComponent<{ handleOpen: () => void }>;
  children: React.ReactNode;
};

export function Modal({ activator: Activator, children }: ModalProps) {
  const [isVisible, setVisible] = useState(false);

  return (
    <View>
      <DefaultModal
        visible={isVisible}
        transparent={false}
        animationType={"slide"}
      >
        <View style={styles.contentView}>
          {children}
          <Button onPress={() => setVisible(false)} title="Close"></Button>
        </View>
      </DefaultModal>
      {Activator ? (
        <Activator handleOpen={() => setVisible(true)} />
      ) : (
        <Button onPress={() => setVisible(true)} title="Open"></Button>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  contentView: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
  },
});

 

Screen.tsx

 

import { View, Text, StyleSheet, Button } from "react-native";
import { Modal } from "../components/styled/Modal";

export default function Screen() {
  return (
    <View style={styles.container}>
      <Modal
        activator={({ handleOpen }) => (
          <Button onPress={handleOpen} title="Open"></Button>
        )}
      >
        <Text>Hello This is a activator modal view</Text>
      </Modal>

      {/* <Modal>
        <Text>Hello This is a modal view</Text>
      </Modal> */}
    </View>
  );
}

728x90
๋ฐ˜์‘ํ˜•