모달을 구현하면서 props
전달을 최소화하고 싶었다. 컴포넌트가 한 단계 차이라면 상관없지만, 내가 원한 구현은 A
컴포넌트에서 버튼을 클릭하면 B
안에 있는 C
모달이 열리는 구조였다.
일반적이라면 A -> B
로 state
와 setState
를 보내고, B -> C
로 setState
를 보내야 한다. 얼마나 꼴뵈기 싫은가…
고민하던 차에 책에서 봤던 Context
가 생각났다. props
를 전달하지 않고 해당 컴포넌트에서 직접 호출하기 위해 사용했다.
Context
는 React 컴포넌트 트리 안에서 전역적(global)이라고 볼 수 있는 데이터를 공유할 수 있도록 고안된 방법이다.
// modalOpen.js
import { createContext, useState } from "react";
const ModalOpen = createContext({
state: { isOpen: false },
actions: {
setIsOpen: () => {},
},
});
const ModalProvider = ({ children }) => {
const [isOpen, setIsOpen] = useState(false);
const value = {
state: { isOpen },
actions: {
setIsOpen,
},
};
return <ModalOpen.Provider value={value}>{children}</ModalOpen.Provider>;
};
const { Consumer: ModalConsumer } = ModalOpen;
export { ModalProvider, ModalConsumer };
export default ModalOpen;
먼저 모달의 현재 상태와 제어 함수가 담긴 context를 생성했다.
Context
는 사용할 컴포넌트를 감싸는 provider
와 사용하는 cunsumer
가 필요하다. provider
에서 context의 state
와 setState
를 정의했다.
// index.js
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { ModalProvider } from "./context/open";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<ModalProvider>
<App />
</ModalProvider>
</React.StrictMode>
);
전역에서 사용할 수 있도록 <App />
컴포넌트를 provider
로 감쌌다.
차례차례 useContext
를 호출하여 필요한 props
만 사용했다.
// App.js
import { useContext } from "react";
import ModalOpen from "./context/open";
import Table from "./Table";
export default function App() {
const {
actions: { setIsOpen },
} = useContext(ModalOpen);
return (
<div>
<Table />
<button onClick={() => setIsOpen(true)}>열기</button>
</div>
);
}
App
컴포넌트에서는 모달을 여는데 필요한 액션만 호출했다.
// Table.js
import { useContext } from "react";
import ModalOpen from "./context/open";
import Modal from "./Modal";
export default function Table() {
const {
state: { isOpen },
} = useContext(ModalOpen);
return (
<div>
<h3>Table</h3>
{isOpen && <Modal />}
</div>
);
}
Table
컴포넌트에서는 모달을 여는 조건인 state
만 호출했다.
import { useContext } from "react";
import styled from "styled-components";
import ModalOpen from "./context/open";
const Overview = styled.div`
width: 100vw;
height: 100vh;
position: absolute;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.7);
`;
const Box = styled.div`
width: 500px;
height: 300px;
background-color: white;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
`;
export default function Modal() {
const {
actions: { setIsOpen },
} = useContext(ModalOpen);
return (
<Overview>
<Box>
<h1>hello!</h1>
<button onClick={() => setIsOpen(false)}>닫기</button>
</Box>
</Overview>
);
}
Modal
컴포넌트에서는 다시 닫아야 하므로 액션을 호출했다.
멀리 떨어진 컴포넌트로의 props
전달이 싫어서 새로 배운 Context
를 사용해 봤다. 더 좋은 방법이 있겠지? 더 고민해 보자.
참고
도서 - 『리액트를 다루는 기술』, 김민준(velopert), 길벗
React Dos - Context