Context API 정리

rkdghwnd·2023년 5월 19일
0

  • Context API는 Context를 만들어 전역에서 상태를 관리하는 API
  • 기존에 컴포넌트 전역에서 상태를 사용하기 위해서는 최상위 컴포넌트에서 상태를 관리해야 했음
    -> props drilling 발생, 상태 흐름이 복잡해짐
  • Context API는 컴포넌트 외부에 Context라는 전역상태관리 저장소를 만들어 상태를 관리하는 방식
  • 전역상태관리 방식은 Context API외에 Redux나 MobX 등의 라이브러리도 존재함

1. 새 Context 만들기

npx create-react-app context-tutorial

createContext 를 통해 Context를 생성한다.

src/contexts/color.js

import {create Context} from 'react';

const ColorContext = createContext({ color: 'black'})

export default ColorContext;

2. Consumer 사용하기

  • createContext를 이용해 생성한 Context를 컴포넌트 형태로 가져와 Consumer를 사용할 수 있다.
  • Consumer를 이용해 전역 Context에 있는 상태를 가져와 사용할 수 있다.
  • Consumer 컴포넌트의 children으로 함수를 삽입하고, 렌더링할 태그를 작성하는 방식(Render Props)

/src/components/ColorBox.js

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

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

export default ColorBox;

App.js

import React from "react";
import ColorBox from "./components/Colorbox";
const App = () => (
  <div>
    <ColorBox />
  </div>
);

export default App;

3. Provider (정적인 방식으로 Context의 상태 업데이트)

  • eateContext를 이용해 생성한 Context를 컴포넌트 형태로 가져와 Provider를 사용할 수 있다.
  • Provider를 이용해 Context의 상태를 변경할 수 있다.
  • Provider에 꼭 value 값을 넣어 상태를 변경해야 사용할수 있다.(안하면 에러)
import React from "react";
import ColorBox from "./components/Colorbox";
import ColorContext from "./contexts/color";
const App = () => (
  <ColorContext.Provider value={{ color: "red" }}>
    <div>
      <ColorBox />
    </div>
  </ColorContext.Provider>
);

export default App;

4. 동적으로 Context의 상태 업데이트 하기

4-1. Consumer와 Provider 사용하기 좋게 정리
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 },
  };

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

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

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

export default ColorContext;

App.js

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

function App() {
  return (
    <ColorProvider>
      <div>
        <ColorBox />
      </div>
    </ColorProvider>
  );
}

export default App;

src/components/ColorBox.js

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

4-2 색상 선택 컴포넌트 UI 준비
src/components/SelectColor.js

import React from "react";

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

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

export default SelectColors;

App.js

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

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

export default App;

4-3 이벤트 핸들러를 통해 색상변경(상태변경)

  • context에 정의했던 actions를 이용해 상태를 변경할수 있다.
  • Consumer로 actions를 가져와서 상태를 변경한다.
  • 왼쪽클릭(onClick)은 큰 박스의 색상을 변경, 오른쪽 클릭(onContextMenu)은 작은 박스의 색상을 변경하도록 작성한다.
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;

5. useContext Hook, static contextType

5-1. useContext

  • 함수 컴포넌트에서는 useContext를 이용해 Context API의 RenderProps 패턴을 탈피할수 있다.
    src/components/ColorBox.js
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;

5-2. static contextType

  • 클래스 컴포넌트에서는 static contextType를 이용하면 RenderProps 패턴을 탈피할수 있다.
  • static contextType를 사용하면 this.context를 통해 context의 상태에 접근할 수 있다.

src/components/SeelctColors.js

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

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

class SelectColors extends Component {
  static contextType = ColorContext;

  handleSetColor = (color) => {
    this.context.actions.setColor(color);
  };

  handleSetSubColor = (subcolor) => {
    this.context.actions.setSubcolor(subcolor);
  };

  render() {
    return (
      <div>
        <h2>색상을 선택하세요</h2>
        <div style={{ display: "flex" }}>
          {colors.map((color) => (
            <div
              key={color}
              style={{
                background: color,
                width: "24px",
                height: "24px",
                cursor: "pointer",
              }}
              onClick={() => this.handleSetColor(color)}
              onContextMenu={(e) => {
                e.preventDefault();
                this.handleSetSubColor(color);
              }}
            />
          ))}
        </div>
        <hr />
      </div>
    );
  }
}

export default SelectColors;
profile
rkdghwnd's dev story

0개의 댓글