티스토리 뷰

TIL

Zustand 이용해 컨펌창 만들기

윤미주 2024. 6. 24. 03:59

* 가장 하단에 참고 블로그가 있습니다. 참고한 블로그를 보고 제가 이해한 대로 재작성 하였습니다. 제가 이해한대로 작성하였으니 잘 모르시겠다면 하단에 참고 블로그를 확인해주세요. 간단 명료하게 잘 작성해주셨습니다.

 

전역상태 관리로 zustand를 사용하고 있어 zustand를 이용해 컨펌창을 만들어보려고 한다.

 

confirm() 을 호출하면 기대하는 동작

1. confirm 다이얼로그 띄우기

2. 다이얼로그가 닫히면 결과 반환.

 

confirm 다이얼로그를 띄우기 위해서는 modal과 같이 boolean값으로 상태를 변경시키고,

상호작용이 끝나는 시점에 결과 값을 받기 위해서는 ` Promise `(대기, 완료, 실패) 되었을때 그 결과를 ` resolve `를 통해 반환받으면 된다.

 

confirm창의 동작을 관리하는 커스텀 훅 만들기

1. interface를 이용해 confirm 상태 정의하기

  message: string;

confirm창을 사용하는 곳마다 다른 메시지를 보여줄 수 있도록 하기위해 작성.

 

  showDialog: boolean;

confirm창의 보이기/숨기기를 관리할 수 있도록 boolean 값으로 작성

 

  confirm: (message?: string) => Promise<boolean>;

사용자에게 메시지를 표시하고 사용자에게 true 또는 false를 Promise로 반환하는 함수

 

  onClickOK: () => void;
  onClickCancel: () => void;

사용자가 true 또는 false를 클릭했을 때 실행되는 함수

 

 

2. confirm 훅 만들기

export const useConfirmStore = create<ConfirmState>((set) => ({
}));

create를 사용하여 set을 통해 매개변수를 받을 수 있도록 해주면 된다.

 

  message: "",
  showDialog: false,

message의 초기상태는 `" "`빈문자열, showDialog의 초기상태는 `false`로 설정해 기본적으로 보이지 않게 하자.

 

이제 message를 매개변수로 받고, Promise를 반환할 수 있는 함수를 만들어 주어야 한다.

 

  confirm: (message) => {
    return new Promise((resolve) => {
      set({
        message: message ?? "",
        showDialog: true,
        onClickOK: () => {
          set({ showDialog: false });
          resolve(true);
        },
        onClickCancel: () => {
          set({ showDialog: false });
          resolve(false);
        },
      });
    });
  },
  onClickOK: () => {},
  onClickCancel: () => {},

 

confirm은 message를 매개변수로 받고 Promise를 반환한다.

 

set을 통해 message를 매개변수로 받고 showDialog를 true가 된다. 

`onClickOK` 이 클릭되었을 때 true를 반환하고  confirm창을 숨기게한다.

`onClickCancel` 이 클릭되었을 때 false를 반환하고 confirm창을 숨기게 한다.  

 

 

<confirm 훅 전체코드>

import { create } from "zustand";

//컨펌 훅
interface ConfirmState {
  message: string;
  showDialog: boolean;
  confirm: (message?: string) => Promise<boolean>;
  onClickOK: () => void;
  onClickCancel: () => void;
}

export const useConfirmStore = create<ConfirmState>((set) => ({
  message: "",
  showDialog: false,
  confirm: (message) => {
    return new Promise((resolve) => {
      set({
        message: message ?? "",
        showDialog: true,
        onClickOK: () => {
          set({ showDialog: false });
          resolve(true);
        },
        onClickCancel: () => {
          set({ showDialog: false });
          resolve(false);
        },
      });
    });
  },
  onClickOK: () => {},
  onClickCancel: () => {},
}));

 

 

confirm창 컴포넌트 만들기

  const { message, showDialog, onClickOK, onClickCancel } = useConfirmStore();

만들어둔 confirm 훅을 import해온다.

 

if (!showDialog) return null;

confirm창이 flase인 경우 null을 반환해 confirm창을 렌더링하지 않고 조건적으로 보여줄 수 있도록 한다.

 

<div className="fixed inset-0 bg-black bg-opacity-40 z-40"></div>

confirm창이 보였을 때 다른 부분은 선택되지 않을 수 있도록 confirm창 뒤로 div를 깔아준다.

 

        <div className="px-8 py-8 md:py-10 md:px-10 bg-white rounded-md">
          <p className="flex justify-start items-center text-black text-lg">
            {message}
          </p>
          <div className="flex flex-row justify-end gap-4 mt-10 w-full md:h-10">
            <button
              className="hover-color w-20 md:w-24 font-semibold border whitespace-nowrap border-border-color px-4 py-1 rounded-md"
              onClick={onClickCancel}
            >
              아니오
            </button>
            <button
              className="bg-point-color md:w-24 font-semibold text-black hover-color w-20 border whitespace-nowrap border-border-color px-4 py-1 rounded-md"
              onClick={onClickOK}
            >
              예
            </button>
          </div>
        </div>

실제 confirm창이 될 div 부분을 그려주면 된다.

 

          <p className="flex justify-start items-center text-black text-lg">
            {message}
          </p>

confirm훅에서 만들어둔 messeage를 보여주고 

 

            <button
              className="hover-color w-20 md:w-24 font-semibold border whitespace-nowrap border-border-color px-4 py-1 rounded-md"
              onClick={onClickCancel}
            >
              아니오
            </button>

아니오를 보여주는 버튼에는 onClickCancel을 주고

            <button
              className="bg-point-color md:w-24 font-semibold text-black hover-color w-20 border whitespace-nowrap border-border-color px-4 py-1 rounded-md"
              onClick={onClickOK}
            >
              예
            </button>

예를 보여주는 버튼에는 onClickOK를 주면 끝!

 

confirm 컴포넌트 전체코드

import { useConfirmStore } from "@/store/commenStore";
import React from "react";

export default function DoubleCheckConfirm() {
  const { message, showDialog, onClickOK, onClickCancel } = useConfirmStore();

  if (!showDialog) return null;

  return (
    <>
      <div className="fixed inset-0 bg-black bg-opacity-40 z-40"></div>
      <div
        className="overflow-hidden bg-white rounded-3xl fixed left-1/2 transform -translate-x-1/2 z-50 w-80 md:w-[480px]"
        style={{ top: "16%" }}
      >
        <div className="px-8 py-8 md:py-10 md:px-10 bg-white rounded-md">
          <p className="flex justify-start items-center text-black text-lg">
            {message}
          </p>
          <div className="flex flex-row justify-end gap-4 mt-10 w-full md:h-10">
            <button
              className="hover-color w-20 md:w-24 font-semibold border whitespace-nowrap border-border-color px-4 py-1 rounded-md"
              onClick={onClickCancel}
            >
              아니오
            </button>
            <button
              className="bg-point-color md:w-24 font-semibold text-black hover-color w-20 border whitespace-nowrap border-border-color px-4 py-1 rounded-md"
              onClick={onClickOK}
            >
              예
            </button>
          </div>
        </div>
      </div>
    </>
  );
}

 

 

실제 사용하는 곳(일부)

  import DoubleCheckConfirm from "../common/DoubleCheckConfirm";
  
  const handleDelete = async () => {
    const isConfirmed = await confirm("정말 삭제하시겠습니까?");
    if (isConfirmed) {
      deleteHomeDetailMutate(id);
      router.replace("/category/re-home");
    }
  };
  
 return  <DoubleCheckConfirm />

async await을 이용해 confirm을 사용하면 된다.

async, await 을 사용하는 이유는 confirm 함수가 Promise<boolean>을 반환하기 때문에 결과를 기다리고 처리하기 위해 사용한다!

참고 블로그 ( https://velog.io/@foreknowledge/React-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EB%A1%9C-alert-confirm-prompt-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0 )