// 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
에서 모달 컴포넌트를 넣거나 제거하도록 구현하면 된다.