스파르타코딩클럽/내일배움캠프

Zustand 모달 open/close

myinfo7091 2024. 12. 27. 10:59

 

Zustand를 사용해서 모달 상태를 간단하고 효율적으로 관리할 수 있다. Zustand는 경량 상태 관리 라이브러리로, 전역 상태와 모달의 열림/닫힘 상태 관리에 적합하다.

 

 

1. Zustand 스토어를 설정해서 모달의 상태와 상태 변경 함수를 정의한다.

import create from 'zustand';

interface ModalState {
  isOpen: boolean;
  openModal: () => void;
  closeModal: () => void;
}

const useModalStore = create<ModalState>((set) => ({
  isOpen: false,
  openModal: () => set({ isOpen: true }),
  closeModal: () => set({ isOpen: false }),
}));

export default useModalStore;

 

 

2. 설정한 Zustand 스토어를 사용해서 모달의 열림/닫힘 상태를 제어한다.

import React from 'react';
import useModalStore from '../store/useModalStore';

const Modal = () => {
  const { isOpen, closeModal } = useModalStore();

  if (!isOpen) return null;

  return (
    <div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
      <div className="bg-white p-6 rounded shadow-md">
        <h2 className="text-lg font-bold">Modal Title</h2>
        <p>이것은 모달 내용입니다.</p>
        <button
          onClick={closeModal}
          className="mt-4 px-4 py-2 bg-blue-500 text-white rounded"
        >
          닫기
        </button>
      </div>
    </div>
  );
};

export default Modal;

 

 

3. Shadcn/ui dialog 에서 사용하기

 

Dialog의 기본 닫기 버튼(DialogClose)을 사용하도록 변경하려면, 커스텀 닫기 버튼을 제거하고 DialogClose를 활용하면된다. 기본 닫기 버튼은 Radix UI 기반으로 제공되며, shadcn/ui에서 이미 스타일이 적용되어 있기 때문에 추가 작업 없이 바로 사용할 수 있다. 이렇게 편할 수가.

 

 

  • onOpenChange는 상태를 관리하는 openModal 및 closeModal 함수와 동기화되어, 기본 닫기 버튼을 클릭해도 상태가 업데이트된다.
'use client';

import React, { useState } from 'react';
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogClose,
} from "@/components/ui/dialog";
import ChatList from './ChatList';
import ChatRoom from './ChatRoom';
import useModalStore from '../store/modalStore';

const ChatModal = () => {
  const { isOpen, openModal, closeModal } = useModalStore();
  const [activeTab, setActiveTab] = useState<'list' | 'room'>('list');
  const [activeChatroomId, setActiveChatroomId] = useState<string | null>(null);

  return (
    <>
      {/* 플로팅 채팅 버튼 */}
      <button
        className="fixed bottom-5 right-5 p-4 bg-blue-600 text-white rounded-full shadow-lg focus:outline-none"
        onClick={openModal}
      >
        💬
      </button>

      {/* 채팅 모달 */}
      <Dialog open={isOpen} onOpenChange={(open) => (open ? openModal() : closeModal())}>
        <DialogContent>
          {/* 모달 헤더 */}
          <DialogHeader>
            <DialogTitle>Chat</DialogTitle>
            <DialogClose className="text-gray-500 hover:text-gray-700 focus:outline-none" />
          </DialogHeader>

          {/* 탭 */}
          <div className="flex border-b">
            <button
              className={`flex-1 py-2 text-center ${
                activeTab === 'list' ? 'border-b-2 border-blue-500 font-semibold' : ''
              }`}
              onClick={() => setActiveTab('list')}
            >
              채팅 리스트
            </button>
            <button
              className={`flex-1 py-2 text-center ${
                activeTab === 'room' ? 'border-b-2 border-blue-500 font-semibold' : ''
              }`}
              onClick={() => setActiveTab('room')}
            >
              채팅방
            </button>
          </div>

          {/* Content */}
          <div className="flex-1 overflow-y-auto">
            {activeTab === 'list' ? (
              <ChatList onSelectChatroom={setActiveChatroomId} />
            ) : (
              <ChatRoom chatroomId={activeChatroomId} />
            )}
          </div>
        </DialogContent>
      </Dialog>
    </>
  );
};

export default ChatModal;