ContextAPI

이정수·2023년 9월 6일
0

ReactDeepDive

목록 보기
1/4
post-thumbnail

Context API

Context API는 리액트 프로젝트에서 전역적으로 사용할 데이터가 있을경우 유용한 기능이다.
예를 들어 '로그인정보, 테마, 어플리케이션 환경설정'등이 있다. 많이 사용되는 리덕스, 리액트라우터, styled-components의 라이브러리도 ContextAPI를 기반으로 구현되어 있다.

전역상태의 관리 흐름

이전에는 프로젝트내엥서의 환경설정, 사용자 정보와 갔는 전역적으로 필요한 상태를 관리해야 할때, 컴토넌트 간에 데이터를 props로 전달하기 때문에 컴포넌트 여러군데애서 필요한 데이터가 있을 경우 최상위 컴포넌트인 APP의 state에 넣어서 관리했다.

G 컴포넌트에서 전역적 상태를 업데이트하고 F,J에서 업데이트된 상태를 렌더링해야 한다고 치자.
APP컴포넌트에서는 아래 코드에서 처럼 상태와 업데이트 함수를 정의해야 한다.

const [value, setValue] = useState('hello');
const onSetvalue = useCallback(value => setValue(value), []);

그리고 이 상태를 F,J에 전달하려면 APP -> A -> C-> F, App -> H -> J의 흐름이다.
실제 프로젝트에서 더 많은 상태를 거쳐야 할때도 있고, 데이터가 훨씬 많아 질 수 도 있으므로 유지보수성의 방면에서는 효율적이지 못할 수 있다.

이처럼 최상위 컴포넌트에서 prop drilling을 통해 상태와 함수를 전달했지만,이제는 Context API를 통해 한번에 원하는 값을 받아와서 사용할 수 있다.

React프로젝트 어딘가에 context라는 상자를 만들어 둔다.
-> 이 context안에 다른 컴포넌트들에게 전달해주고 싶은 데이터들을 넣어둔다.(ex: 유저정보)
-> 특정한 컴포넌트에서 이 context에 연결한다.(표현상)
-> 연결된 컴포넌트의 하위에 있는 모든 컴포넌트들은 context에 직접적으로 접근할 수 있는 권한을 부여받는다.
-> 하위에 있는 모든 컴포넌트들중 아무나 간단한 문법으로 이 컨텍스트에 잇는 값을 사용할것이라고 적어주면 된다.

Context API 사용법

1. 새로운 Context 만들기

src/context/color 파일을 만들고 안에 아래 코드를 작성한다.
(반드시 context 디렉토리를 만들 필요는 없다.)

import { createContext } from "react";

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

export default ColorContext;

새로운 Context를 만들때는 createContext함수를 사용한다. 파라미터에는 Context의 "기본상태"를 지정한다.

2. Consumer로 사용하기

ColorBox라는 컴포넌트를 만들어서 ColorContext안에 있는 color를 보여주도록 하자. 이때는 props로 받아오는 것이 아닌 ColorContext안에 들어있는 Consumer라는 컴포넌트를 통해 색상을 조회할것이다.

src/components/Colorbox

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;

이제 APP에서 이를 랜더링한다.

function App() {
  console.log('App 컴포넌트 랜더링');
  return (
      <ColorBox />
  )
}

export default App

-> 검은색 정사각형이 잘 뜨는 것을 확인할 수 있다.

3. Provider

Provider를 사용하면 Context의 value를 변경할 수 있다.
APP 컴포넌트를 수정하여 검은색이 아닌 빨간색 박스를 만들어 보자

import ColorContext from "./contexts/color";
function App() {
  console.log('App 컴포넌트 랜더링');
  return (
    <ColorContext.Provider value ={{color: 'red'}} >
      <ColorBox />
    </ColorContext.Provider>
  )
}

export default App

앞서 createContext 함수에서는 파라미터로 기본값(검은색)을 넣어주었다. 이 기본값은 Provider를 사용하지 않았을 때만 사용된다.
Provider를 사용하려면 반드시 value값을 명시해 줘야 한다.

** provider로 감싸져있는 하위 컴포넌트들 (props.children) 에서만 contex에 접근 및 사용이 가능하다.

동적 Context 사용하기

이번에는 고정적인 값이 아닌 Context의 값을 업데이트 해야하는 경우 어떻게 해야 하는지 알아보자

Context 파일 수정

Context의 value에는 상태값 뿐만 아니라 함수도 전달해 줄 수 있다.

ColorContext의 코드를 다음과 같이 수정

import { createContext, useState } from "react";


const ColorContext = createContext({
  state : {color: 'black', subcolor: 'red'},
  actions: {
    setColor : () => {},
    setSubcolor: () => {}
  }
});

const ColorProvider = ({children}:any) => {
  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;

export { ColorProvider, ColorConsumer}

export default ColorContext;

ColorProvider라는 새로운 컴포넌트를 작성하였다. 이 컴포넌트에서는 ColorContext.Provider를 랜더링하는데,
Provider의 value에는 상태는 state로 업데이트함수는 actions로 묶어서 전달한다. Context에서 값을 동적으로 사용할때 반드시 묶어줄 필요는 없으나,
state, actions객체를 따로 분리해주면 다른 컴포넌트에서 Context의 값을 사용할 때 편리하다.

(* typescript에서 사용시 Provider의 value파라미터를 전달하는 부분에서 에러가 발생한다. 이럴때는 setState를 바로 넘겨주는 것이아니라 setState를 실행하는 함수를 전달해서 우회하는 것이 좋다는 구글링 결과를 얻었다.)

색상선택 컴포넌트 만들기

//Context의 Actions에 넣어 준 함수를 호출하는 컴포넌트
//마우스 왼쪽버튼 => 큰 정사각형 색상 변경,

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>
          ))}
      </div>)}
      </ColorConsumer>
    </div>
  )
}
export default SelectColors;

ColorBox 컴포넌트 : ColorContext.Consumer를 ColorConsumer로 변경

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

useContext Hook 사용하기

리액트 내장 Hook 중 useContext Hook을 사용하여 함수컴포넌트에서 Context를 편하게 사용할 수 있다.

ColorBox 컴포넌트의 코드를 좀더 간단하게 다음과 같이 수정할 수 있다.

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

profile
keep on pushing

0개의 댓글