[React] Context API 사용하기

🌊·2021년 12월 26일
0

React

목록 보기
15/20

Context API

프로젝트 내에서 환경 설정, 사용자 정보와 같은 전역적으로 필요한 상태를 관리해야 할 때 사용한다.

리액트 애플리케이션은 컴포넌트 간에 데이터를 props로 전달하기 때문에 여기저기서 필요한 데이터가 있을 때는 주로 최상위 컴포넌트인 App의 state에 넣어서 관리한다.

이러한 상황에서는 컴포넌트 목적지까지 많은 컴포넌트를 거쳐야 할 때도 있고 다루어야하는 데이터가 훨씬 많아질수도 있기 때문에 유지보수성이 낮아질 가능성이 있다.

리덕스나 MobX와 같은 상태 관리 라이브러리를 사용하여 전역 상태 관리 작업을 더 편하게 처리하기도 한다.
리액트 v16.3 업데이트 이후에는 Context API가 많이 개선되었기 때문에 라이브러리를 사용하지 않아도 전역 상태를 손쉽게 관리할 수 있다.

Context API를 사용하면 Context를 만들어 단 한 번에 원하는 값을 받아 와서 사용할 수 있다.

Context 생성

contexts/color.js

import { createContext } from "react";

const ColorContext = createContext({ color: "black" });

export default ColorContext;

Consumer 사용하기

components/ColorBox.js

import ColorContext from "../contexts/color";

// ColorContext 안에 들어있는 Consumer라는 컴포넌트를 통해 색상을 조회한다.
const ColorBox = () => {
  return (
    <ColorContext.Consumer>
      {(value) => (
        <div
          style={{
            width: "64px",
            height: "64px",
            background: value.color,
          }}
        ></div>
      )}
    </ColorContext.Consumer>
  );
};

export default ColorBox;

컴포넌트를 만들어서 Context안에 있는 색상을 가져올 수 있다.
Consumer 사이에 중괄호를 넣어서 그 안에 함수를 넣어준다.
이러한 패턴을 function as a child 혹은 Render Props라고 한다.

컴포넌트의 children이 있어야 할 자리에 일반 JSX 혹은 문자열이 아닌 함수를 전달하는 것이다.

Provider 사용하기

Provider를 사용하면 Context의 value를 변경할 수 있다.

App.js

import ColorBox from "./components/ColorBox";
import ColorContext from "./contexts/color";

const App = () => {
  return (
    <ColorContext.Provider value={{ color: "red" }}>
      <div>
        <ColorBox />
      </div>
    </ColorContext.Provider>
  );
};

export default App;

Context에서 지정한 black 색상이 Provider를 통해서 red로 변경되었다.
createContext 함수에서 사용하는 파라미터('black')은 기본값이다.
Provider를 사용하지 않았을 때만 사용된다.
Provider를 사용할 때는 value 값을 명시해 주어야 제대로 작동한다.

동적 Context 사용하기

contexts/color.js

import { createContext, useState } from "react";

const ColorContext = createContext({
  state: { color: "black", subcolor: "red" },
  actions: {
    setColor: () => {},
    setSubcolor: () => {},
  },
});

const ColorProvider = ({ children }) => {
  const [color, setColor] = useState("black");
  const [subcolor, setSubcolor] = useState("red");

  const value = {
    state: { color, subcolor }, // 상태
    actions: { setColor, setSubcolor }, // 업데이트 함수
  };
  // Context에서 값을 동적으로 사용할 때 반드시 묶어 줄 필요는 없지만,
  // state와 actions 객체를 따로 분리해주면 나중에 다른 컴포넌트에서 Context 값을 사용할 때 편리하다.

  return <ColorContext.Provider value={value}>{children}</ColorContext.Provider>;
};

// const ColorConsumer = ColorContext.Consumer와 같은 의미
const { Consumer: ColorConsumer } = ColorContext;

// ColorProvider와 ColorConsumer 내보내기
export { ColorProvider, ColorConsumer };

export default ColorContext;

ColorProvider라는 컴포넌트를 새로 작성했다.
ColorContext.Provider를 렌더링하고 있다.
이 Provider의 value에서 상태는 state로 업데이트 함수는 actions로 묶어서 전달하고 있다.

App.js

import ColorBox from "./components/ColorBox";
import SelectColors from "./components/SelectColors";
import { ColorProvider } from "./contexts/color";

const App = () => {
  return (
    <ColorProvider>
      <div>
        <SelectColors />
        <ColorBox />
      </div>
    </ColorProvider>
  );
};

export default App;

components/ColorBox.js

import ColorConsumer from "../contexts/color";

// ColorContext 안에 들어있는 Consumer라는 컴포넌트를 통해 색상을 조회한다.
const ColorBox = () => {
  return (
    <ColorConsumer>
      {(value) => (
        <>
          <div
            style={{
              width: "64px",
              height: "64px",
              background: value.state.color,
            }}
          />
          <div
            style={{
              width: "32px",
              height: "32px",
              background: value.state.subcolor,
            }}
          />
        </>
      )}
    </ColorConsumer>
  );
};

export default ColorBox;

색상 선택 컴포넌트 만들기

components/SelectColors.js

import { ColorConsumer, colorConsumer } from "../contexts/color";

const colors = ["red", "orange", "yellow", "green", "blue", "indigo", "violet"];

const SelectColors = () => {
  return (
    <div>
      <h2>색상을 선택하세요.</h2>
      <ColorConsumer>
        {({ actions }) => (
          <div style={{ display: "flex" }}>
            {colors.map((color) => (
              <div
                key={color}
                style={{ background: color, width: "24px", height: "24px", cursor: "pointer" }}
                onClick={() => actions.setColor(color)}
                onContextMenu={(e) => {
                  e.preventDefault();
                  actions.setSubcolor(color);
                }}
              />
            ))}
          </div>
        )}
      </ColorConsumer>
    </div>
  );
};

export default SelectColors;

Consumer 대신 Hook 사용하기

useContext Hook 사용하기

components/ColorBox.js

import { useContext } from "react";
import ColorContext from "../contexts/color";

// ColorContext 안에 들어있는 Consumer라는 컴포넌트를 통해 색상을 조회한다.
const ColorBox = () => {
  const { state } = useContext(ColorContext);
  return (
    <>
      <div
        style={{
          width: "64px",
          height: "64px",
          background: state.color,
        }}
      />
      <div
        style={{
          width: "32px",
          height: "32px",
          background: state.subcolor,
        }}
      />
    </>
  );
};

export default ColorBox;

children에 함수를 전달하는 Render Props 패턴이 불편하다면, useContext Hook을 사용하여 훨씬 편하게 Context 값을 조회할 수 있다.
Hook은 함수 컴포넌트에서만 사용할 수 있다는 것을 주의하자.

0개의 댓글