[React] Context

찐새·2022년 9월 6일

React

목록 보기
11/21
post-thumbnail

Context

모달을 구현하면서 props 전달을 최소화하고 싶었다. 컴포넌트가 한 단계 차이라면 상관없지만, 내가 원한 구현은 A 컴포넌트에서 버튼을 클릭하면 B 안에 있는 C 모달이 열리는 구조였다.

일반적이라면 A -> B로 state와 setState를 보내고, B -> C로 setState를 보내야 한다. 얼마나 꼴뵈기 싫은가…

props drilling

고민하던 차에 책에서 봤던 Context가 생각났다. props를 전달하지 않고 해당 컴포넌트에서 직접 호출하기 위해 사용했다.

Context 생성

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를 정의했다.

provider

// 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

차례차례 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

profile
프론트엔드 개발자가 되고 싶다

0개의 댓글