[React] forwardRef로 modal 관리하기

SangBooom·2021년 11월 24일
0
post-custom-banner

기존 모달

모달 사용 컴포넌트에서 visible 상태를 관리하고 상태를 토글할 수 있는 함수를 관리합니다.
모달 컴포넌트는 visible 상태값, 상태를 토글할 수 있는 함수를 props로 받습니다.

단점: 모달을 사용할 때 마다 상태, 함수를 따로 관리해야된다.

forwardRef를 사용한 모달

모달 사용 컴포넌트는 ref를 관리
모달 컴포넌트는 useImperativeHandle과 ref를 통해서 모달안에서 visible 상태 관리를 함

장점: 독립적으로 모달이 상태를 관리하기 때문에 props로 넘겨줄 필요가 없음.

onCancel 있는 버전

// 모달 사용 컴포넌트

import React, { useCallback } from 'react';
import KaKaoModal, { RefProps } from '@atoms/Modal/KaKaoModal';

const Index = (): React.ReactElement => {
  const tempSaveRef = React.useRef<RefProps>(null);

  const goToKakaoLogin = useCallback(() => {
    console.log('카카오로그인으로 이동');
  }, []);

  const clickModal = useCallback(() => {
    if (tempSaveRef.current) tempSaveRef.current.handleVisible();
  }, []);

  const onClickCancel = useCallback(() => {
    console.log('모달 창 꺼짐과 동시에 실행');
  }, []);

  return (
    <>
      <button onClick={clickModal}>모달 열기</button>
      <KaKaoModal ref={tempSaveRef} onClickOk={goToKakaoLogin} onClickCancel={onClickCancel} />
    </>
  );
};

export default Index;
// 모달

import React, { useState, forwardRef, useImperativeHandle, useCallback } from 'react';

import { ModalWrapper } from './KaKaoModal.styles';

interface Props {
  onClickOk: () => void;
  onClickCancel?: () => void;
}

export interface RefProps {
  handleVisible: () => void;
}

const KaKaoModal = ({ onClickOk, onClickCancel }: Props, ref: React.Ref<RefProps>) => {
  const [visible, setVisible] = useState(false);

  const toggleVisible = useCallback(() => {
    setVisible((prev) => {
      if (!prev) return !prev;
      if (onClickCancel) {
        onClickCancel();
      }
      return !prev;
    });
  }, [onClickCancel]);

  useImperativeHandle(ref, () => ({
    handleVisible: () => toggleVisible(),
  }));

  return (
    <ModalWrapper
      visible={visible}
      destroyOnClose
      closable={false}
      footer={null}
      width={448}
      centered
      onCancel={toggleVisible}
    >
      <div onClick={toggleVisible}>취소</div>
      <div onClick={onClickOk}>ok</div>
    </ModalWrapper>
  );
};

export default forwardRef(KaKaoModal);

onCancel 없는 버전

// 모달 사용 컴포넌트

import React, { useCallback } from 'react';
import KaKaoModal, { RefProps } from '@atoms/Modal/KaKaoModal';

const Index = (): React.ReactElement => {
  const tempSaveRef = React.useRef<RefProps>(null);

  const goToKakaoLogin = useCallback(() => {
    console.log('카카오로그인으로 이동');
  }, []);

  const clickModal = useCallback(() => {
    if (tempSaveRef.current) tempSaveRef.current.handleVisible();
  }, []);

  return (
    <>
      <button onClick={clickModal}>모달 열기</button>

      <KaKaoModal ref={tempSaveRef} onClickOk={goToKakaoLogin} />
    </>
  );
};

export default Index;
// 모달

import React, { useState, forwardRef, useImperativeHandle, useCallback } from 'react';

import { ModalWrapper } from './KaKaoModal.styles';

interface Props {
  onClickOk: () => void;
}

export interface RefProps {
  handleVisible: () => void;
}

const KaKaoModal = ({ onClickOk }: Props, ref: React.Ref<RefProps>) => {
  const [visible, setVisible] = useState(false);

  const toggleVisible = useCallback(() => {
    setVisible((prev) => !prev);
  }, []);

  useImperativeHandle(ref, () => ({
    handleVisible: () => toggleVisible(),
  }));

  return (
    <ModalWrapper
      visible={visible}
      destroyOnClose
      closable={false}
      footer={null}
      width={448}
      centered
      onCancel={toggleVisible}
    >
      <div onClick={toggleVisible}>취소</div>
      <div onClick={onClickOk}>ok</div>
    </ModalWrapper>
  );
};

export default forwardRef(KaKaoModal);
profile
끊임없이 떨어지는 물방울이 바위를 뚫는다
post-custom-banner

0개의 댓글