// src/MyModal.js
const myModal = ({ isOpen, onCancel, onSubmit }) => {
return (
<Modal isOpen={isOpen}>
<h1>모달입니다.</h1>
<div>
<button onClick={onSubmit}>확인</button>
<button onClick={onCancel}>취소</button>
</div>
</Modal>
)
}
App 컴포넌트의 특정 버튼을 눌러 모달 컴포넌트를 열어주고 싶다고 하자.App 컴포넌트의 state로 관리하게 된다.props로 전달한다.// src/App.js
import { useState } from "react";
import MyModal from "./MyModal";
const App = () => {
const [isOpen, setOpen] = useState(false);
const handleClick = () => {
setOpen(true);
}
const onSubmit= () => {
// 특정 로직
setOpen(false);
}
const onCancel = () => {
setOpen(false);
}
return (
<>
<button onClick={handleClick}>모달 열기</button>
<MyModal isOpen={isOpen} onSubmit={onSubmit} onCancel={onCancel}/>
</>
);
}
state도 여러 개 필요하다. (useState 증가)props로 전달해야 한다.props로 모달을 열어주는 함수를 전달한다.props 제거)💡 핵심 원인은 모달 관리 방법의 파편화이다. 모달이 필요한 컴포넌트별로 각 모달 컴포넌트를 렌더링하고, 모달의 열고 닫음 상태를
state로 스스로 관리하기 때문에 발생한다. 파편화된 모달 관리를 한 곳으로 모아 중앙화된 모달 관리를 구현해보자!
모달의 열고 닫음 상태를 모달이 필요한 컴포넌트에서 직접 state로 관리했다면 이제는 Context API를 이용하여 관리하자!
💡 현재 open된 모달 컴포넌트를 나타내는 state
const openedModals = [
{
Component,
props
},
{
Component,
props
},
...
];
💡 모달을 열고 닫는 함수
openedModals에 모달 컴포넌트 추가openedModals에 모달 컴포넌트 제거// ModalsContext.js
import { createContext } from "react";
// 현재 open된 modal들을 나타냄.
export const ModalsStateContext = createContext([]);
// modal을 열고 닫는 함수
export const ModalsDispatchContext = createContext({
open: () => {}
close: () => {}
});
// ModalsProvider.js
import { useState } from "react";
import { ModalsStateContext, ModalsDispatchContext } from "./ModalsContext";
const ModalsProvider = ({children}) => {
const [openedModals, setOpenedModals] = useState([]);
const open = (Component, props) => {
setOpenedModals((modals) => {
return [...modals, { Component, props }];
});
}
const close = (Component) => {
setOpenedModals((modals) => {
return modals.filter(modal => modal.Component !== Component);
});
}
const dispatch = {open, close};
return (
<ModalsDispatchContext.Provider value={dispatch}>
<ModalsStateContext.Provider value={opendedModals}>
{children}
</ModalsStateContext.Provider>
</ModalsDispatchContext.Provider>
);
}
// index.js
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import ReactModal from "react-modal";
import App from "./App";
import ModalsProvider from "./ModalsProvider";
ReactModal.setAppElement("#root");
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<ModalsProvider>
<App />
</ModalsProvider>
</StrictMode>,
rootElement
);
ModalsDispatchContext의 open 함수와 close 함수를 이용하면 된다.ModalsStateContext의 openedModals를 이용하여 open된 모달들을 렌더링하는 컴포넌트를 만들어보자// Modals.js
import { useContext } from "react";
import { ModalsDispatchContext, ModalsStateContext } from "./ModalsContext";
const Modals = () => {
const openedModals = useContext(ModalsStateContext);
const { close } = useContext(ModalsDispatchContext);
return openedModals.map((modal, index) => {
const { Component, props } = modal;
const { onSubmit, ...restProps } = props;
const onClose = () => {
close(Component);
}
const handleSubmit = async () => {
if (typeof onSubmit === "function") {
await onSubmit();
}
onClose();
};
return (
<Component
key={index}
onClose={onClose}
handleSubmit = {handleSubmit}
{...restProps}
/>
);
});
}
export default Modals;
// useModals.js
import { useContext } from "react";
import { ModalsDispatchContext } from "./ModalsContext";
export default function useModals() {
const { open, close } = useContext(ModalsDispatchContext);
const openModal = (Component, props) => {
open(Component, props);
};
const closeModal = (Component) => {
close(Component);
};
return { openModal, closeModal };
}
// App.js
import "./styles.css";
import useModals from "./useModals.js";
import Modals from "./Modals";
import MyModal from "./MyModal";
export default function App() {
const { openModal } = useModals();
const handleClick = () => {
openModal(MyModal, {
onSubmit: () => {
console.log("로직 처리...");
}
});
};
return (
<div className="App">
<button onClick={handleClick}>모달 열기</button>
<Modals />
</div>
);
}
✨
Context API를 이용하여 파편화된 모달 관리를 중앙화하여 컴포넌트 외부에서 관리하도록 한다.open된 모달 컴포넌트와 해당 컴포넌트의props를 나타내는 객체 배열을state를 정의한다. 이state에 있는 컴포넌트를 렌더링하는 컴포넌트를 만든다. 그러면 이제 모달을 열고 닫는 함수는 앞서 정의한state에서 모달 컴포넌트를 넣거나 제거하도록 구현하면 된다.