React : Context API

HaByungNo·2022년 10월 14일
1

Today I Learned

목록 보기
11/16
post-thumbnail

지금까지 나는 React를 다루면서 전역 State 관리를 위해 Redux 또는 Redux toolkit 을 사용했었다.

하지만 별도의 라이브러리가 없이도 React에 내장된 Context API를 통해 전역 상태를 손쉽게 관리할 수 있다고 한다.
부끄럽게도 Context API의 존재로 모르르고 있었어서... 오늘은 그걸 좀 알아보려고 한다.

Context API 사용법

import { createContext } from "react";

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

export default ColorContext

새로운 Context를 만들때는 createContext() 함수를 사용한다. 파라미터에는 InitialState(기본값) 를 지정한다.

그리고 전역 상태를 사용할 컴포넌트를 준비한다. 나는 ColorBox 라고 만들었다.

import React from 'react'
import ColorContext from '../contexts/DarkMode'

const ColorBox = () => {
  return (
	<div></div>
  )
}

export default ColorBox

위에서 만든 ColorContext 라는 모듈에서 Consumer 라는 컴포넌트를 통해 color 를 가져오려고 한다.

<ColorContext.Consumer>
{
 ...
}
</ColorContext.Consumer>

이렇게 사이에 중괄호를 열어서 ... 에 jsx 문법으로 함수를 전달해보자

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

그리고 랜더링할 컴포넌트에 ColorBox 컴포넌트를 넣는데
여기서는 App.js에 랜더링 한다.

import React from 'react';
import ColorBox from './components/ColorBox';

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

export default App;

코드를 실행해보면 까만 정사각형 하나를 볼 수 있을것이다.
왜냐면 기본값으로 {color: "black"} 을 줬으니까

하지만 여기서 끝나면 안됀다.
전역 state 관리를 하려면 물론 값을 변경할 수 있어야 하지 않겠는가?
Consumer 말고 Provider 를 사용하면 Context 의 value를 변경할 수 있다.

import React, { useState } from "react";
import ColorBox from "./components/ColorBox";
import ColorContext from "./contexts/darkMode";

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

export default App;

위처럼 <ColorContext.Provider/> 컴포넌트에 value에 원하는 값을 넣으면 검정 정사각형은
빨간 정사각형으로 변해있을 것이다.

createContext 함수에서 파라미터로 준 초기값은 Provider를 사용하지 않을때만 사용된다.



동적 Context 사용법

Context 값을 업데이트 하려면 어떻게 해야할까?
예시로 하트를 클릭할 때마다 숫자가 올라가는 좋아요 기능을 만들어 보겠다.

import React from 'react';

import './App.css';

function App() {
  return (
    <div className="App">
      <div className="box">
      <span className="heart"></span>
      <span className="num">0</span>
      </div>
    </div>
  );
}

export default App;

프로젝트를 생성하고 대충 css 만져서 좋아요랑 숫자 표시되는 UI 를 만들었다.

이제 해야할건 Context 를 만들어주면 된다


import { createContext } from "react";

const LikeContext = createContext({
  state : { isLike : false, number: 0},
  action: {
    setLike: () => {},
    setNumber: () => {},
  }
});

export default LikeContext;

LikeContext 라는 Context 를 만들고 초기값으로 위와 같이 넣어 주었다.
이후 useState 를 사용해서 action 함수 부분을 채워줄 것이다.

그리고 바로 아래에 LikeProvider 컴포넌트를 만든다.

import { createContext, useState } from "react"; // useState import!

const LikeContext = createContext({
  state: { isLike: false, number: 0 },
  action: {
    setLike: () => {},
    setNumber: () => {},
  },
});

// 추가된 코드
const LikeProvider = ({ children }) => {
  const [like, setLike] = useState(false);
  const [number, setNumber] = useState(0);

  const value = {
    state: { like, number },
    action: { setLike, setNumber },
  };

  return <LikeContext.Provider value={value}>{children}</LikeContext.Provider>;
};const LikeConsumer = LikeContext.Consumer

export {LikeProvider, LikeConsumer}

export default LikeContext;

LikeProvider 컴포넌트에서는 LikeContext.Provider 를 랜더링한다. 이 Provider의 value 는 상태는 state로 업데이트 함수는 action 으로 전달된다.

꼭 이렇게 해야하는 건 아니지만 이렇게 state와 action 을 분리해 놓으면 나중에 다른 컴포넌트에서 Context 값을 가져올때 편하다.

import React from "react";

import "./App.css";
import { LikeConsumer, LikeProvider } from "./contexts/like";

function App() {
  return (
    <LikeProvider>
      <LikeConsumer>
        {(value) => (
          <div className="App">
            {console.log(value.state.like)}
            <div className="box">
              {value.state.like ? (
                <span
                  onClick={() => {
                    value.action.setLike(!value.state.like);
                    value.action.setNumber(value.state.number - 1);
                  }}
                  className="heart"
                ></span>
              ) : (
                <span
                  onClick={() => {
                    value.action.setLike(!value.state.like);
                    value.action.setNumber(value.state.number + 1);
                  }}
                  className="heart"
                ></span>
              )}
              <span className="num">{value.state.number}</span>
            </div>
          </div>
        )}
      </LikeConsumer>
    </LikeProvider>
  );
}

export default App;

<LikeConsumer> 로 UI를 감싼 후 중괄호 안에 Render props 함수를 넣는다. 그리고 <LikeProvider><LikeConsumer> 를 감싼후 알맞게 데이터 바인딩을 해준다.

조건부 랜더링과 onClick 이벤트에 대한 설명은 생략한다.

간단한 좋아요 기능이 완성되었다.




코드 개선

작성 중...

profile
프라고

0개의 댓글