15장 Context API (리액트를 다루는 기술)

김지원·2020년 11월 20일
0

React

목록 보기
19/31

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

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

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

새 Context 만들기

import { createContext } from "react";

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

export default ColorContext;

새 Context를 만들 때는 createContext 함수를 사용합니다.
파라미터에는 해당 Context의 기본 상태를 지정합니다.

Counsumer 사용하기

import React from 'react';
import ColorContext from "../contexts/color";

const ColorBox = () => {
    return (
        <ColorContext.Consumer>
            {value =>(
                <div
                    style={{
                    width: '64px',
                    height:'64px',
                    background: value.color
                    }}/>
            )}
        </ColorContext.Consumer>
    );
};

export default ColorBox;

ColorBox라는 컴포넌트를 만들어서 ColorContext 안에 들어 있는 색상을 보여 주겠습니다.
이때 색상을 props로 받아 오는 것이 아니라 ColorContext 안에 들어 있는 Consumer 라는 컴포넌트를 통해 색상을 조회할 것 입니다.

Consumer 사이에 중괄호를 열어서 그 안에 함수를 넣어 주었습니다.

이러한 패턴을 Function as a child, 혹은 Render Props라고 합니다.

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

Provider

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

import React from "react";
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;

실행하면 빨간색 정사각형으로 변경!

처음에 createContext 함수를 사용할 때는 파라미터로 Context의 기본값을 넣어주었습니다. 이 기본값은 Provider를 사용하지 않았을 때만 사용됩니다.

Provider를 사용하고 value를 명시하지 않는다면 기본값을 사용하지 않기 때문에 오류가 발생합니다.

Provider를 사용할 때는 value 값을 명시해 주어야 제대로 작동합니다!

동적 Context 사용하기

Context의 값을 업데이트해야 하는 경우

Context 파일 수정하기

Context의 value에는 무조건 상태 값만 있어야 하는 것은 아닙니다.
함수를 전달해 줄 수도 있습니다.

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 },
  };

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

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

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

export default ColorContext;

컴포넌트에서 ColorContext.Provider를 렌더링하고 있습니다.
이 Provider의 value에는 상태는 state로, 업데이트 함수는 actions로 묶어서 전달하고 있습니다.

Context에서 값을 동적으로 사용할 때는
이렇게 state와 actions 객체를 따로따로 분리해 주면 나중에 다른 컴포넌트에서 Context의 값을 사용할때 편합니다.

새로워진 Context를 프로젝트에 반영하기

기존의 App 컴포넌트에서 ColorContext.Provider를 ColorProvider로 대체

ColorBox도 ColorContext.Consumer를 ColorConsumer로 변경해주고,

import React from "react";
import { ColorConsumer } from "../contexts/color";

const ColorBox = () => {
  return (
    <ColorConsumer>
      {({ state }) => (
        <>
          <div
            style={{
              width: "64px",
              height: "64px",
              background: state.color,
            }}
          />
          <div
            style={{
              width: "32px",
              height: "32px",
              background: state.subcolor,
            }}
          />
        </>
      )}
    </ColorConsumer>
  );
};

export default ColorBox;

실행 화면

색상 선택 컴포넌트 만들기

import React from "react";
import { 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>
      <hr />
    </div>
  );
};

export default SelectColors;



실행 화면

Consumer 대신 Hook 또는 static contextType 사용하기

Context에 있는 값을 사용할 때 Consumer 대신 다른 방식을 사용하여 값을 받아오는 방법

useContext Hook 사용하기

리액트에 내장되어 있는 Hooks 중에서 useContext라는 Hook을 사용하면, 함수형 컴포넌트에서 Context를 아주 편하게 사용할 수 있습니다.

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

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 값을 조회할 수 있습니다.

static contextType 사용하기

클래스형 컴포넌트에서 Context를 좀 더 쉽게 사용하고 싶다면 static ContextType을 정의하는 방법이 있습니다.

0개의 댓글

관련 채용 정보