React Context
1. 개요
리액트 앱을 개발하다보면 모든 컴포넌트(최상위 컴포넌트에서 최하위 컴포넌트)에 특정 값을 props로 넘기고 싶을 때가 있습니다. 전역(global)로 선언된 데이터가 있으면 참 편할텐데 말이죠.
이 Context는 리엑트 컴포넌트 트리 안에서 Global한 데이터를 공유할 수 있도록 고안된 방법입니다.
이러한 데이터는 보통 현재 로그인한 유저, 적용중인 테마, 선호하는 언어 등이 있을 수가 있습니다.
단계적으로 props를 전달하지 않아도 Context를 사용하면 Global한 데이터를 효율적으로 사용할 수가 있겠죠.
2. 일반적인 props의 사용
아래의 코드는 버튼 컴포넌트를 꾸미기 위해 테마 props를 명시적으로 넘겨줍니다.
class App extends React.Component {
render() {
return <Toolbar theme="dark" />;
}
}
function Toolbar(props) {
// Toolbar 컴포넌트는 불필요한 테마 prop를 받아서 ThemeButton에 전달해야 합니다.
// 앱 안의 모든 버튼이 테마를 알아야 한다면
// 이 정보를 일일이 넘기는 과정은 매우 곤혹스러울 수 있습니다.
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
class ThemedButton extends React.Component {
render() {
return <Button theme={this.props.theme} />;
}
}
App에서 dark라고 지정한 테마를 Toolbar가 받고, ThemedButton이 받고, render의 Button에서 theme가 지정이 되었습니다.
이렇게 최상위 구성요소에서 도착할 하위 구성요소까지 불필요한 작업을 계속하는 것을 Props Drilling이라고 하는데, context를 사용할 경우 중간에 있는 엘리먼트에게 전달할 필요가 없어집니다!
3. 사용방법
createContext()함수를 사용해서 전역에서 데이터를 관리합니다.
// context를 사용하면 모든 컴포넌트를 일일이 통하지 않고도
// 원하는 값을 컴포넌트 트리 깊숙한 곳까지 보낼 수 있습니다.
const ThemeContext = React.createContext('light');
class App extends React.Component {
state = { theme: “light” }
changeTheme = () => {
this.setState({theme}) => ({
theme: theme === “light” ? “dark” : “light”,
}));
};
render() {
// Provider를 이용해 하위 트리에 테마 값을 보내줍니다.
// 아무리 깊숙히 있어도, 모든 컴포넌트가 이 값을 읽을 수 있습니다.
// 아래 예시에서는 dark를 현재 선택된 테마 값으로 보내고 있습니다.
return (
const { theme } = this.state;
<ThemeContext.Provider value={theme}>
<Toolbar />
</ThemeContext.Provider>
);
}
}
// 이젠 중간에 있는 컴포넌트가 일일이 테마를 넘겨줄 필요가 없습니다.
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// 현재 선택된 테마 값을 읽기 위해 contextType을 지정합니다.
// React는 가장 가까이 있는 테마 Provider를 찾아 그 값을 사용할 것입니다.
// 이 예시에서 현재 선택된 테마는 dark입니다.
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
Context를 사용할 때 주의해야할 것은 Provider입니다.
Context로 지정한 데이터를 적용할 컴포넌트를 Context.Provider로 감싸줘야 한다는 점이 중요합니다!
이렇게 Provider로 감싸주면 그 하위에 있는 모든 컴포넌트들은 이 Context에 저장되어 있는 전역 데이터에 접근할 수가 있습니다.
value는 Context의 값을 지정하는 변수고, 값을 지정하지 않을 경우 Context를 생성할 때 넘겼던 디폴트 값이 사용됩니다.
4. 다른예시
이번에는 전체적인 테마 색상을 버튼을 누르면 바뀌는 예시를 보겠습니다.
ThemeContext.js
import { createContext } from "react";
const ThemeContext = createContext("light");
export default ThemeContext;
App.js
import React, { Component } from "react";
import ThemeContext from "./ ThemeContext ";
import ThemeButton from "./ ThemeButton ";
import Title from "./Title";
import Message from "./Message";
class App extends Component {
state = { theme: "light" };
changeTheme = () => {
this.setState(({theme }) => ({
theme: theme === "light" ? "dark" : "light"
}));
};
render() {
const { theme } = this.state;
return (
<ThemeContext.Provider value={theme}>
<ThemeButton changeTheme={this.changeTheme } />
<Title />
<Message />
</ThemeContext.Provider>
);
}
}
export default App;
Context를 사용할 수 있는 방법은 3가지가 있습니다.
1) useContext로 Context에 접근
ThemeButton.js
import { useContext, useState } from "react";
import ThemeContext from './ThemeContext'
export default function ThemeButton({changeTheme}){
const theme = useContext(ThemeContext);
return(
<button onClick={changeTheme}>{theme}</button>
)
}
changeTheme 함수를 테마버튼 컴포넌트에 props로 넘겨주고, 테마버튼 컴포넌트에서는 useContext()함수를 이용해 전역데이터에 접근을 했습니다.
테마버튼 컴포넌트 useContext함수에 인자로 ThemeContext를 넘김으로 현재 테마의 상태값을 읽습니다.
이 useContext함수는 함수형 컴포넌트에서만 사용할 수 있습니다!
2) Consumer로 Context에 접근
Provider와 대응하는 Consumer를 이용해 Context의 전역데이터를 읽어오는데, render props를 받기 때문에 하위 자식(childern)으로 넘기는 방법으로 Context의 값을 읽어옵니다.
Title.js
import React from "react";
import LangContext from "./LangContext";
function Title() {
return (
<LangContext.Consumer>
{lang => {
const text = lang === "light" ? "Context" : "컨텍스트";
return <h1>{text}</h1>;
}}
</LangContext.Consumer>
);
}
export default Title;
3) contextType으로 Context에 접근
클래스로 구현된 컴포넌트에서는 static contextType을 이용해서 전역으로 지정된 context를 초기화하고, this.context로 값을 읽어올 수 있습니다.
Message.js
import React, { Component } from "react";
import LangContext from "./LangContext";
class Message extends Component {
static contextType = LangContext;
render() {
const lang = this.context;
if (lang === "light")
return (
<p>
"Context provides a way to pass data through the component tree
without having to pass props down manually at every level"
</p>
);
else
return (
<p>
"컨텍스트는 모든 레벨에서 일일이 props를 넘기지 않고도 컴포넌트 트리에
걸쳐서 데이터를 전달할 수 있는 방법을 제공합니다."
</p>
);
}
}
export default Message;
참고
'FrontEnd > React 기본' 카테고리의 다른 글
[React] React.js 강좌 13. useState (0) | 2022.03.07 |
---|---|
[React] React.js 강좌 12. useRef (0) | 2021.12.10 |
[React] React.js 강좌 10. map, slice, concat, spread 사용법 (0) | 2021.12.07 |
[React] React.js 강좌 9. Input 상태 관리하기 (0) | 2021.11.26 |
[React] React.js 강좌 8. 함수형 컴포넌트 props 정리 (0) | 2021.11.25 |