[리엑트를 다루는 기술] Chapter 15 : Context API

Context API란?

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

Context API를 사용한 전역 상태 관리 흐름 이해하기

🤔 전역적을 사용하는 데이터는 어떻게 관리해야할까?

기존에는

⇒ 리액트는 컴포넌트 간에 데이터르 props로 전달하기에(부모 → 자식) 최상위 컴포넌트에 데이터를 저장을 한다

⇒ 하단에 있는 컴포넌트(G)가 상태값을 변경하고, 다른 하단에 있는 컴포넌트(F)가 상태 값을 업데이트 해야하면, 변경된 값이 다시 순차적으로 최상단 부터 → F까지 이어지게 되는 복잡함이 생긴다

유지 보수성이 떨어진다!

contextAPI를 이용하면 이러한 데이터 흐름이 편해진다

Context API 사용법 익히기

Cosumer 익히기

// color.js
import { createContext } from "react";

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

export default ColorContext;

//ColorBox.js
import React from 'react';
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.js
import React from 'react'
import ColorBox from './components/ColorBox'
function App() {
  return (
    <div>
      <ColorBox />
    </div>
  )
}

export default App
  • ColorContxet.Consumer에 함수를 인자로 넣어(value값에 따라 사각형이 보이는) Consumer에 할당된 value인 color에 접근해 black을 할당한다 이러한 패턴을 Render Props라 부른다
const RenderPropsSample = ({ children }) => {
  return <div>결과: {children(5)}</div>;
};

export default RenderPropsSample;

 

만약 위와 같은 컴포넌트가 있다면 추후 사용할 때 다음과 같이 사용할 수 있습니다.

<RenderPropsSample>{value => 2 * value}</RenderPropsSample>;
 

RenderPropsSample에게 children props로 파라미터에 2를 곱해서 반환하는 함수를 전달하면 해당 컴포넌트에서는 이 함수에 5를 인자로 넣어서 “결과: 10“을 렌더링합니다.

Provider 익히기

Context의 value를 변경할수 있다

import React from 'react'
import ColorBox from './components/ColorBox'
import ColorContext from './contexts/color'
function App() {
  return (
    <ColorContext.Provider value={{ color: 'red' }}>
      <div>
        <ColorBox />
      </div>
    </ColorContext.Provider>

  )
}

export default App
  • Context에 할당된 value가 변경되었다
  • Provider를 선언했는데 value를 할당해주지 않으면 오류가 난다

동적 Context 사용하기

지금까지는 고정 값만 사용할수 있었다.

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

Context 파일 수정하기

  • 값만 넣을필요없다, 함수도 가능하다

state의 value 값을 변경해자

// Color.js
import React, { 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';
const App = () => {
  return (
    <ColorProvider>
      <div>
        <ColorBox />
      </div>
    </ColorProvider>
  );
};

export default App;

//ColorBox.js
import React from 'react';
import { ColorConsumer } from '../contexts/color';

const ColorBox = () => {
    return (
        <ColorConsumer>
            {value => ( // value = {state} 로 설정하면, 비구조화 할당이된다.
                <>
                    <div
                        style={{
                            width: '64px',
                            height: '64px',
                            background: value.state.color
                        }}
                    />
                    <div
                        style={{
                            width: '32px',
                            height: '32px',
                            background: value.state.subcolor
                        }}
                    />
                </>
            )}
        </ColorConsumer>
    );
};

export default ColorBox;

색상 선택 컴포넌트 만들기

action으로 함수를 호출해보자

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;
  • 이제 행동에 따라 value의 상태 값이 변한다

즉 결론적으로 contextAPI을 이용하기 위해서는 provider로 감싸고, 그 하부에 있는 컴포넌트들은 consumner로 value or action 받아와, 사용할수 있다

하지만 앞서 context파일을 수정해서. 보기 쉽게 사용할수 있게 되었다


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

consumer 대신 다른 방식으로 값을 받아오자

[함수형] useContext이용하기

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;

useContext으로 접근

import React from 'react';
import { ColorConsumer } from '../contexts/color';

const ColorBox = () => {
    return (
        <ColorConsumer>
            {value => ( // value = {state} 로 설정하면, 비구조화 할당이된다.
                <>
                    <div
                        style={{
                            width: '64px',
                            height: '64px',
                            background: value.state.color
                        }}
                    />
                    <div
                        style={{
                            width: '32px',
                            height: '32px',
                            background: value.state.subcolor
                        }}
                    />
                </>
            )}
        </ColorConsumer>
    );
};

Consumer, Reder porps 패턴으로 접근

비교해보자

함수형 컴포넌트에서 useContext를 사용하면 state값으로 바로 접근해 사용할수있다

[클래스형] static contextType

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);
//나중에 action에 접근할게 많아면
//이렇게 간단화 시키자 const actions = this.context.actions //// 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;

static으로 다른 함수들이 contextType에 접근하게 하자, contextType = Colorcontext 이니, action 접근해서 상용하면 된다.

  • 하지만 contextType가 static으로 객체로 받지 못하니, 하나에 하나의 context밖에 받지 못하는 단점..

좋은 웹페이지 즐겨찾기