고윤태의 개발 블로그

누구나 접근하기 쉽지만 얻어 가는 것이 있는 글을 쓰기 위해 노력 중입니다.

개발

React Native useModal 구현하기

고윤태 2023. 5. 2. 11:50

안녕하세요 이번에 회사 앱에 flow를 깊게 확인하기 위해 이것저것 해보는 와중 제 눈에 띈 것이 하나 있습니다.

Modal

바로 Modal 입니다.

Modal은 앱에서 흔히 볼 수 있는 UI입니다.

Modal을 사용하는 곳에서는 항상 기본적으로 state 1개와 함수 2개로 구성이 되어있었습니다.

const [isModalOpened, setIsModalOpened] = useState(false);

const closeModal = () => {
  setModalOpened(false);
};

const openModal = () => {
  setModalOpened(true);
};

이런 똑같은 코드가 반복이 되는 것이 보기가 싫어 리팩토링을 해보도록 하겠습니다.

구현 단계

1차 구현

interface ModalHandle {
  openModal: () => void;
  closeModal: () => void;
}
const useModal = (): [boolean, ModalHandle] => {
  const [isModalOpened, setIsModalOpened] = useState<boolean>(false);

  const closeModal = (): void => {
    setIsModalOpened(false);
  };
  const openModal = (): void => {
    setIsModalOpened(true);
  };

  const modalHandle: ModalHandle = {
    openModal,
    closeModal,
  };
  return [isModalOpened, modalHandle];
};
export default useModal;

단순하게 Modal의 state 값과 handler(open, close)만 return 하고 있습니다.

이제 반복되는 코드를 지울 수 있겠다고 생각했었습니다.
유저 입장에서 놓친 부분이 있을까? 하고 고민을 하던 도중 놓친 케이스가 떠올랐습니다.

안드로이드 유저 같은 경우 backbutton을 통해 Modal을 닫는 유저도 존재합니다.

android



바로 backbutton에 대한 케이스를 정리했습니다.

CASE

  • backbutton을 클릭 시 Modal을 close 한다.
    • 기존에 backbutton 클릭 시 이벤트가 존재한다면 Modal이 닫혀있는 경우라면 그 이벤트를 실행해야 한다.
  • backbutton을 클릭 시 Modal을 close 하고 싶지 않은 경우도 존재할 수 있다.
    • UX적으로 사용자가 꼭 확인해야 하는 Modal이라면 이런 경우도 존재할 수 있다는 가능성을 열어놨습니다.

위에 적은 CASE를 고려하여 다시 구현을 해주도록 하겠습니다.

최종 구현

import { useState, useEffect } from 'react';
import { BackHandler } from 'react-native';
interface ModalOptions {
  preventClose?: boolean;
  onBackHandler?: () => boolean;
}
interface ModalHandle {
  openModal: () => void;
  closeModal: () => void;
}
const useModal = (options?: ModalOptions): [boolean, ModalHandle] => {
  const [isModalOpened, setIsModalOpened] = useState<boolean>(false);
  const closeModal = (): void => {
    if (options?.preventClose) {
      return;
    }
    setIsModalOpened(false);
  };
  const openModal = (): void => {
    setIsModalOpened(true);
  };
  useEffect(() => {
    const backAction = (): boolean => {
      if (options?.preventClose) {
        return true;
      }
      if (isModalOpened) {
        closeModal();
        return true;
      }
      if (options?.onBackHandler) {
        return options.onBackHandler();
      }
      return false;
    };
    const originalBackHandler = BackHandler.addEventListener('hardwareBackPress', backAction);
    const originalCallback = originalBackHandler.remove;
    originalBackHandler.remove = (): void => {
      if (isModalOpened) {
        closeModal();
      } else {
        originalCallback();
      }
    };
    return (): void => originalBackHandler.remove();
  }, [isModalOpened, options?.onBackHandler, options?.preventClose]);
  const modalHandle: ModalHandle = {
    openModal,
    closeModal,
  };
  return [isModalOpened, modalHandle];
};
export default useModal;


코드 설명

options를 통해 2개의 파라미터를 받습니다.

  • preventClose : Modal이 열려있을 때 backbutton을 누를 시 Modal을 닫을지 말지 여부를 결정하는 boolean 값입니다
  • onBackHandler : Modal이 닫혀있을 때 backbutton을 누를 시 실행할 함수입니다.

'개발' 카테고리의 다른 글

useDebounce를 활용하기  (1) 2023.06.07
React Native Chip을 구현해보자  (0) 2023.05.26
React Native 나만의 checkbox 구현하기 ✅  (0) 2023.05.16
내 입맛에 맞게 useList를 구현하기  (0) 2023.05.10
useCodePush 구현하기  (3) 2023.04.24