지금까지 나는 React
를 다루면서 전역 State 관리를 위해 Redux
또는 Redux toolkit
을 사용했었다.
하지만 별도의 라이브러리가 없이도 React에 내장된 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 값을 업데이트 하려면 어떻게 해야할까?
예시로 하트를 클릭할 때마다 숫자가 올라가는 좋아요 기능을 만들어 보겠다.
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 이벤트에 대한 설명은 생략한다.
간단한 좋아요 기능이 완성되었다.
작성 중...