리액트 모달 팝업2

리액트 모달 팝업2

QA

리액트 모달 팝업2

본문

모달 작동 방식의 대한 이해도가 조금 생겼습니다.

그런데 제가 구현하고자 하는 것은 예를 들면

 

main.jsx 에 모달버튼이 있고


import React, { useState } from "react";
 
function Main() {
   
    // 상태 관리를 위한 useState 훅
    const [open, setOpen] = useState(false);
   
    return (
        <div className="container">
            <div className="visual">
                <div className="txt">
                    <h1>메인 비주얼</h1>
                    <p>메인 비주얼 서브 텍스트 입니다. 메인 비주얼 서브 텍스트 입니다.</p>
                </div>
            </div>
            <div className="inner">
                <section className="section1">
                    <ul>
                        <li><button onClick={() => setOpen(true)}>모달1 테스트</button></li>
                        <li><button onClick={() => setOpen(true)}>모달2 테스트</button></li>
                        <li><button onClick={() => setOpen(true)}>모달3 테스트</button></li>
                    </ul>
                </section>
            </div>
        </div>
    );
}
 
export default Main;

 

app.jsx 에 모달 출력 부분이 있는데 main.jsx 에서 모달버튼을 클릭하면 app.jsx 에 모달 출력부분을

동작시켜야 하지 않나요? 해당 위치에서만 모달을 열 수 있다면 모달이 많아지면 관리가 불가능할거 같아서 app.jsx 에 모든 모달 출력 부분을 다 모아놓고 작동시킬려고 하다보니... 


import 'assets/css/common.css';
import 'assets/css/style.css';
 
import { Routes, Route } from 'react-router-dom';
import Layout from 'layout/layout';
import Main from 'page/main';
import Sub01 from 'page/sub01';
import Sub02 from 'page/sub02';
import Sub03 from 'page/sub03';
 
// 모달
import React, { useState } from "react";
import { ModalPortal } from "component/modalPortal";
import { Modal_1 } from "component/modal";
import { Modal_2 } from "component/modal";
import { Modal_3 } from "component/modal";
 
function App() {
 
  // 상태 관리를 위한 useState 훅
  const [open, setOpen] = useState(false);
 
  return (
    <div>
      <Routes>
        <Route element= {<Layout />}>
          <Route path="/" element={<Main />} />
          <Route path="sub01" element={<Sub01 />} />
          <Route path="sub02" element={<Sub02 />} />
          <Route path="sub03" element={<Sub03 />} />
        </Route>
      </Routes>
      <ModalPortal>
          <Modal_1 isOpen={open} onClose={() => setOpen(false)}>
            {/* children 영역 */}
            <div className='modal_body'>
              <h2>모달 1 타이틀</h2>
              <p>이 항목을 삭제하시겠습니까?</p>
              <div className='modal_footer'>
                <button>삭제</button>
                <button onClick={() => setOpen(false)}>
                  취소
                </button>
              </div>
            </div>
          </Modal_1>
          <Modal_2 isOpen={open} onClose={() => setOpen(false)}>
            {/* children 영역 */}
            <div className='modal_body'>
              <h2>모달 2 타이틀</h2>
              <p>이 항목을 삭제하시겠습니까?</p>
              <div className='modal_footer'>
                <button>삭제</button>
                <button onClick={() => setOpen(false)}>
                  취소
                </button>
              </div>
            </div>
          </Modal_2>
          <Modal_3 isOpen={open} onClose={() => setOpen(false)}>
            {/* children 영역 */}
            <div className='modal_body'>
              <h2>모달 3 타이틀</h2>
              <p>이 항목을 삭제하시겠습니까?</p>
              <div className='modal_footer'>
                <button>삭제</button>
                <button onClick={() => setOpen(false)}>
                  취소
                </button>
              </div>
            </div>
          </Modal_3>
      </ModalPortal>
    </div>
  );
}
 
export default App;

 

modal.jsx 


import React from "react";
 
export const Modal_1 = ({ isOpen, onClose, children }) => {
  // 만약 isOpen이 false이면 null을 반환하여 모달을 렌더링하지 않음
  if (!isOpen) return null;
 
  return (
    <div onClick={onClose} className="modal_container">
      <div onClick={(e) => e.stopPropagation()} className="modal_content">
        <button onClick={onClose} className="modal-close">닫기</button>
        {children}
      </div>
    </div>
  );
};
 
export const Modal_2= ({ isOpen, onClose, children }) => {
  // 만약 isOpen이 false이면 null을 반환하여 모달을 렌더링하지 않음
  if (!isOpen) return null;
 
  return (
    <div onClick={onClose} className="modal_container">
      <div onClick={(e) => e.stopPropagation()} className="modal_content">
        <button onClick={onClose} className="modal-close">닫기</button>
        {children}
      </div>
    </div>
  );
};
 
export const Modal_3= ({ isOpen, onClose, children }) => {
  // 만약 isOpen이 false이면 null을 반환하여 모달을 렌더링하지 않음
  if (!isOpen) return null;
 
  return (
    <div onClick={onClose} className="modal_container">
      <div onClick={(e) => e.stopPropagation()} className="modal_content">
        <button onClick={onClose} className="modal-close">닫기</button>
        {children}
      </div>
    </div>
  );
};

 

결론 : app.jsx 에 버튼 + 모달출력영역이 같이 있으면 잘 열리는데 실제 개발시 모달 버튼은 어디 있을지 모른다는거니까요..

 

modal.jsx 모달 껍데기

app.jsx 모달 출력 부분(모달 내용)

 

이런 구조인거 같은데 제가 이해한바로는 ㅎㅎ;;

맞는지 모르겠네요.

 

아무튼 모달들을 app.jsx 에 네이밍 다르게 처리해서 다 정리해놓고

다른 페이지에서 클릭시 해당 네이밍에 모달 열 수는 없나요..?

일반 퍼블에서는 뭐.. 제이쿼리로 data-id 값으로 열듯이 ㅎㅎ;;

 

 

이 질문에 댓글 쓰기 :

답변 4

1. 일단 저렇게 하면 안좋은 습관을 배우시게 됩니다.

 

- 만약 모달이 500개면 컴포넌트 500개 만들건 아니잖아요?

- 따라서 하나의 모달 컴포넌트로 모든걸 제어 해야 합니다

 

2.  main.jsx 에서 그냥 제어 하시거나 / app.jsx 에서 버튼 관리하시면 됩니다.

다음 코드 참조 하세요


 
import './App.css';
import Modal from './components/modal';
import { useState } from 'react';

 
function App() {
  const [isOpen, setIsOpen] = useState([false, false, false]);
 
  const handleModal = (index) => {
    setIsOpen(prevState => {
      const newState = [...prevState];
      newState[index] = !newState[index];
      return newState;
    });
  };
 
  return (
    <>
      {isOpen.map((isModalOpen, index) => (
        <div key={index}>
          <button onClick={() => handleModal(index)}>Open Modal</button>
          <Modal isOpen={isModalOpen} onClose={() => handleModal(index)}>
            <h1>Modal Title {index + 1}</h1>
            <p>Modal Content</p>
          </Modal>
        </div>
      ))}
    </>
  );
}
 
export default App;

 

위 코드는 App.js 인데 main.jsx 파일에서 제어하시던 그건 질문자님이 편하신데로 하시면 됩니다.

반복문을 통해 각각의 배열의 모달창 상태를 관리합니다.

 

이렇게 이해하시면 됩니다.

똑같은 애들을 여러번 만드는데..이게 맞는걸까?

- 1번만 만드시면 됩니다.

- 특히 나도 모르게 xxx_1 / xxx_2 / xxx_3 이렇게 하고 있다면, 100%입니다.

똑같은 애들을 여러개 안만들고 어떻게 제어하지?

- 배열 + 상태를 별도로 관리할 수 있습니다.978180634_1724297947.3679.gif

 

 

자세한 설명 엄청 감사합니다.

근데 제 질문의 의도가 그게 아닌거 같아요.

제 질문의 의도는 예를들어

- 회원가입 페이지에서 아이디중복체크클릭시 해당 모달을 띄워야 할때

- 헤더 부분에서 무언가 클릭시 해당 모달을 띄워야 할때

- 또 어떤 다른 페이지에서 해당 모달을 띄워야 할때 

 

이런 가정하에 회원가입페이지에 버튼이 있어야 하고 헤더 부분에 버튼이 있어야 하고 각각 다른 위치에 버튼이 있어야 하는 상태에서 app.jsx 에는 회원가입페이지에서 클릭한 모달과 헤더 부분에서 클릭한 모달 2가지의 내용이 다른 모달이 있어야 하잖아요?

app.jsx 에는 각기 다른 내용들의 모달들이 있고 각각 다른 페이지에서는 버튼만 있는 상태에서 클릭시 해당하는 모달을 열고 닫는..

제 의도는 이런 뜻이었습니다.

 

각각 다른 내용의 모달들이 app.jsx 에 모여있지만 각각의 버튼들은 다 다른 위치에 있는거죠

그게 리액트에서는 어렵네요... 각각 다른 페이지에서 버튼을 클릭했을 때 app.jsx 에 있는 해당하는 모달을 띄워주는 방법이요..

 

여기서 질문

- app.jsx 에 예제로 해주신것처럼 모달버튼하고 모달내용이 무조건 같은 페이지내에 있어야 하나요?

 

말씀하신 것도 동일합니다.

어디서든 모달이 뜨려면 컴포넌트 하나만 작성하고 하나의 컴포넌트를 상태 변경하여

컨텐츠를 변경하는 식이여야 합니다.

언급하신데로 회원가입 중복 / 이메일 중복 / 닉네임 중복 / 주소 중복 / 우리집 중복 / 강아지 이름 중복 / 저녁 식사 메뉴 중복

하나하나 만들 수 없잖아요?

또, 이번주 이벤트 모달 / 다음 주 이벤트 모달 / 내년 이벤트 모달 / 이렇게

다양하게 있다면, 해당 상황에 맞춰서 모달만 사용하시면 됩니다.

굳이 500개 1000개 띄우는게 아니라, 하나만 쓰시면 됩니다.

안에 들어가는 컨텐츠는 그때 그때 props 나 자식 컨텐츠로 넘겨버리시면 됩니다.

제가 만들고 있는 코드 중 일부만 잠시 보여드리면

 

thumb-978180634_1724313363.3778_730x388.png

 

이거 처럼 필요할 때마다 open 으로 띄우고 그 안에 컨텐츠만 바꾸는 형식으로 개발하시면 됩니다.

그럼 결론은 회원가입 페이지에서 아이디중복체크 버튼 클릭시 모달을 띄울려면 회원가입페이지 해당 위치에 버튼하고 내용담은 모달이 세트로 있어야 한다는거죠? 버튼만 있고 다른 페이지에 있는 모달 컨트럴은 안되는거구요?

확 이해 쉽게 설명 드릴께요

모달 자체를 내가 필요한 곳에 선언 혹은 알림창 함수를 선언 하지 않으면 사용이 안됩니다.

즉... 내가 5층 사는데 1층 주방 식기를 빌려쓴다 선언하면 쓸 수 있지만

선언하지 않으면, 1층 주방 식기를 못 쓰는 것과 같은 이치입니다.

1~5층까지 모든 식기를 사용할 필요 없이

전체 층의 식기를 하나로 만들어서 필요할 때마다 식기를 쓴다 생각하면 이해가 빠르실 것 같습니다.

중복 기준으로 설명 드리면 978180634_1724314002.176.png

 

제껀 이렇게 되어있어요

 

클릭할 때 함수 인자로 뭘 중복하는지 체크 한다음에 모달창 띄워서 그안에 컨텐츠만 바꾸면 되는거니깐요

 

참고로 openNotification 은 알림창 같은거에요 제 기준으로 중복을 모달로 띄우기는 참 뭐해서^^;

 

이런 느낌이라 보시면 될꺼에요

 

978180634_1724314218.9642.gif

답변을 작성하시기 전에 로그인 해주세요.
전체 36
QA 내용 검색

회원로그인

(주)에스아이알소프트 / 대표:홍석명 / (06211) 서울특별시 강남구 역삼동 707-34 한신인터밸리24 서관 1404호 / E-Mail: admin@sir.kr
사업자등록번호: 217-81-36347 / 통신판매업신고번호:2014-서울강남-02098호 / 개인정보보호책임자:김민섭(minsup@sir.kr)
© SIRSOFT