티스토리 뷰

WEB/React

[React] Context API

춘햄 2023. 3. 30. 20:23

Context API는 리액트 프로젝트에서 전역적으로 사용할 데이터가 있을 때 유용한 기능이다. 

 

프로젝트 내에서 환경 설정, 사용자 정보와 같은 전역적으로 필요한 상태를 관리해야 할 때는 어떻게 해야할까? 리액트 App은 컴포넌트 간에 데이터를 props로 전달하기 때문에 컴포넌트 여기저기서 필요한 데이터가 있을 때는 주로 최상위 컴포넌트인 App의 state에 넣어서 관리한다.

 

하지만 최상위 컴포넌트부터 데이터를 전달하는 방식은 다뤄야할 컴포넌트 / 데이터의 수가 많아지면 많아질 수록 사람이 일일이 관리하기가 거의 불가능한 수준까지 갈 수 있다.

 

그렇기 때문에 리덕스나 MobX와 같은 상태 관리 라이브러리를 사용하여 전역 상태 관리 작업을 더 편하게 처리할 수 있다. 

 

리액트 v16.3 업데이트 이후에는 Context API가 많이 개선되었기 때문에 별도의 라이브러리를 사용하지 않아도 전역 상태를 손쉽게 관리할 수 있다.

 

바로 확인해보자.


우선, createContext를 import 하여 아래와 같이 createContext() 함수로 Context의 기본 상태를 지정해준다.

 

◎Color.js

import {createContext} from "react";

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

export default ColorContext;

Consumer

ColorContext 안에 들어 있는 Consumer라는 컴포넌트를 통해 해당 상태를 얻을 수 있다.

 

◎ColorBox.js

import ColorContext from "./Color";

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

export default ColorBox;

단, Consumer를 사용할 때는 컴포넌트 사이에 중괄호를 열어서 그 안에 함수를 전달해줘야 한다.

 

◎App.js

import ColorBox from "./contexts/ColorBox";

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

export default App;


Provider

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

 

◎App.js

import ColorBox from "./contexts/ColorBox";
import ColorContext from "./contexts/Color";

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

export default App;

Provider를 사용할 때는 반드시 value 값을 명시해줘야 한다. 그렇지 않으면 오류를 뱉으니 주의하자.


동적 Context 사용하기

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

 

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

export {ColorProvider, ColorConsumer};

export default ColorContext;

 

◎ColorBox.js

import ColorConsumer from "./Color";

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

export default ColorBox;

 

◎App.js

import ColorBox from "./contexts/ColorBox";
import {ColorProvider} from "./contexts/Color";

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

export default App;

색상 선택 컴포넌트 만들기

이번에는 Context의 actions에 넣어준 함수를 호출하는 컴포넌트를 만들어보자.

 

◎SelectColors.js

import {ColorConsumer} from "./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;

 

◎App.js

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

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

export default App;

이렇게 작성하면 마우스 왼쪽 버튼으로 위 사각형의 색을 변경하고, 오른쪽 버튼으로 아래쪽 사각형의 색을 바꾸면서 Context를 동적으로 바꿀 수 있다.


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

이제는 Context에 있는 값을 사용할 때 Consumer 대신 다른 방식을 사용하여 값을 받아오는 방법을 알아보자.

 

1. useContext Hook 사용하기

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

 

◎ColorBox.js

import ColorContext from "./Color";
import {useContext} from "react";

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

export default ColorBox;

확실히 중괄호가 빠지면서 이전보다 훨씬 간결해졌다. 그러나 Hook은 함수형 컴포넌트에서만 사용이 가능하니, 상황에 맞게 사용해야 한다.

 

2. static contextType 사용하기

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

 

사용할 컴포넌트에서

static contextType = ColorContext;

를 선언해주면, 해당 컴포넌트에서 this.context를 조회했을 때 현재 Context의 value를 가리키게 되기 때문에 아래와 같이 작성해주면 된다.

 

◎SelectColors.js

import {Component} from "react";
import ColorContext from "./Color";

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

class SelectColors extends Component {
    static SelectColors = 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>
                )
            </div>
        );
    }
}

export default SelectColors;

 

 

 

 

끝!

'WEB > React' 카테고리의 다른 글

[React] 리덕스: 리액트 프로젝트에 적용  (0) 2023.03.31
[React] 리덕스: 사전 지식  (0) 2023.03.31
[React] 외부 API 활용: News viewer  (0) 2023.03.30
[React] Routing: React Router  (0) 2023.03.30
[React] 불변성 유지: immer  (0) 2023.03.29
Comments