[React][리액트를 다루는 기술] Context API

uddi·2024년 9월 24일
1

React

목록 보기
19/19

Context API는 전역적으로 사용할 데이터가 있을 떄 유용한 기능이다
ex) 사용자 로그인 정보, 애플리케이션 환경 설정, 테마 등

📌 전역 상태 관리의 필요성

리액트 애플리케이션은 컴포넌트 간 데이터를 props로 전달하기 때문에 주로 최상위 컴포넌트인 APP의 state에 넣어 관리한다

이렇게 되면 컴포넌트가 많아지고 복잡해질 경우 유지 보수성이 낮아진다는 문제가 발생한다

문제를 해결하기 위해 Context AP 같은 상태 관리 라이브러리를 사용한다

📌 Context API 사용법

const { createContext } = require("react");

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

export default ColorContext;

새 Context를 만들 때는 createContext 함수를 사용하고 파라미처에는 해당 Context의 기본 상태를 지정한다

🍞 Consumer 사용

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

export default ColorBox;

Consumer 사이에 중괄호를 열어 그 안에 함수를 넣어 주었다
이러한 패턴을 Function as a child 혹은 Render Props라고 한다

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

✏️ Render Props 예제

const RenderPropsSample = ({ children }) => {
  return <div>결과: {children(5)}</div>;
};

export default RenderPropsSample;

사용법은 다음과 같다
<RenderPropsSample>{value => 2 * value}</RenderPropsSample>

출력 결과는 다음과 같다
결과: 10

🍞 Provider

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

<ColorContext.Provider value={{ color: "red" }}>
  <div>
    <ColorBox />
  </div>
</ColorContext.Provider>

createContex 함수의 파라미터로 넣어준 Context의 기본값은 Provider를 사용하지 않았을 때만 사용된다

Provider는 사용했는데 value를 명시하지 않았다면 오류가 발생한다

📌 동적 Context 사용

🍞 Context 파일 수정

Context의 value에는 상태 값 말고도 함수를 전달해 줄 수도 있다

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 { Consumer: ColorConsumer } = ColorContext;

export { ColorProvider, ColorConsumer };
export default ColorContext;

Provider의 value처럼 state와 actions 객체를 따로따로 분리해 주면 나중에 다른 컴포넌트에서 Context의 값을 사용할 때 편하다 (반드시 묶을 필요는 없다)

createContext의 기본값은 실제 Provider의 value에 넣는 객체의 형태와 일치시켜 주는 것이 좋다

🍞 기존 Context에 반영

ColorContext.Provider를 ColorProvider로 변경하자

<ColorProvider>
  <div>
    <ColorBox />
  </div>
</ColorProvider>

ColorContext.Consumer도 마찬가지로 ColorConsumer로 변경하자

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

🍞 색상 선택 컴포넌트 만들기

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

마우스 오른쪽 버튼 클릭 이벤트는 onContextMenu를 사용하면 된다
원래는 브라우저 메뉴가 나타나지만 e.preventDefault()를 호출하면 뜨지 않는다

📌 Consumer를 대신하는 방식

🍞 useContext Hook

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

Render Props 패턴을 사용할 때보다 훨씬 간결해진 것을 볼 수 있다

🍞 static contextType

static contextType을 정의하는 방법은 클래스형 컴포넌트에서 사용하는 방식이다

클래스 상단에 static contextType = ColorContext;처럼 static contextType 값을 지정하면 this.context를 조회했을 때 현재 Context의 value를 가리키게 된다

  • 장점 : static contextType을 정의하면 클래스 메서드에서도 Context에 넣어 둔 함수를 호출할 수 있음
  • 단점 : 한 클래스에서 하나의 Context밖에 사용하지 못함
profile
거북이는 느리지만 결국 결승선을 통과한다

0개의 댓글