https://velog.io/@velopert/react-context-tutorial
전역적인 상태관리가 필요할 때
예를 들어 로그인한 유저 정보, 테마, language, loacale 정보 등이 있다
props로 컴포넌트 자체를 내려주는 방법도 있다
여러 레벨에 걸쳐서 모든 props를 내려주는 방식에 비해서
가장 상위의 컴포넌트만 구체적인 props를 알아도 되니 좋으나,
반대로 말하면 가장 상위의 컴포넌트의 로직이 복잡해지고 난해해질 수 있다.
function Page(props) {
const user = props.user;
const userLink = (
<Link href={user.permalink}>
<Avatar user={user} size={props.avatarSize} />
</Link>
);
return <PageLayout userLink={userLink} />;
}
// 이제 이렇게 쓸 수 있습니다.
<Page user={user} avatarSize={avatarSize} />
// ... 그 아래에 ...
<PageLayout userLink={...} />
// ... 그 아래에 ...
<NavigationBar userLink={...} />
// ... 그 아래에 ...
{props.userLink}
1. context 객체 만들기
Context
객체의 Provider
로 value
prop으로 넘겨주는 값은 createContext()
의 기본값 보다 우선한다.
// defaultValue: 트리 안에서 적절한 Provider를 찾지 못했을 때만 쓰이는 매개변수
const MyContext = React.createContext(defaultValue);
2. Provider로 컴포넌트들 감싸기
value
prop을 모든 하위 컴포넌트에게 전달합니다.
Provider
하위에 또 Provider
를 배치 할 수 있으며, 이 때 하위 Provider
의 value가 우선된다.
Provider
하위의 모든 컴포넌트들은 value
prop이 바뀔 때, 리렌더링 됩니다.
context
값의 바뀌었는지 여부는 Object.is
와 동일한 알고리즘을 사용해 이전 값과 새로운 값을 비교해 측정됩니다.
<MyContext.Provider value={/* 어떤 값 */}>
Context
객체를 클래스의 contextType
프로퍼티로 지정할 수 있습니다.
이 프로퍼티는 클래스 안에서 this.context
로 읽을 수 있는데 그 값은 해당
Context
의 가장 가까운 Provider
를 찾아서 읽습니다. (= 가장 하위의 Provider
)
this.context
는 모든 생명주기 메서드에서 사용이 가능합니다.
class MyClass extends React.Component {
//static contextType = MyContext; // 정적 프로퍼티로 지정할 수도 있다
componentDidMount() {
let value = this.context;
/* MyContext의 값을 이용한 코드 */
}
componentDidUpdate() {
let value = this.context;
/* ... */
}
componentWillUnmount() {
let value = this.context;
/* ... */
}
render() {
let value = this.context;
/* ... */
}
}
MyClass.contextType = MyContext;
Consumer
로 함수 컴포넌트 안에서도 Context
를 구독할 수 있다.
value
매개변수는 가장 하위의 Provider
의 value
prop과 같다.
만약, Provider
가 없다면 value
매개변수는 createContext()
에서 선언할 때 넣은 기본값과 같다
<MyContext.Consumer>
/* 이 부분은 함수여야 한다 */
{value => /* context 값을 이용한 렌더링 */}
</MyContext.Consumer>
displayName
으로 React Developer Tools
에서 식별되는 Context
의 이름을 지정할 수 있다.
const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';
<MyContext.Provider> // "MyDisplayName.Provider" in DevTools
<MyContext.Consumer> // "MyDisplayName.Consumer" in DevTools
하위 컴포넌트에서 context를 업데이트 하려면 createContext()에서 기본값의
프로퍼티로 업데이트 메소드를 설정해주자
export const ThemeContext = React.createContext({
theme: themes.dark,
toggleTheme: () => {}, // 업데이트 메소드
});
하위 컴포넌트
import {ThemeContext, themes} from './theme-context';
import ThemeTogglerButton from './theme-toggler-button';
class App extends React.Component {
constructor(props) {
super(props);
this.toggleTheme = () => {
this.setState(state => ({
theme:
state.theme === themes.dark
? themes.light
: themes.dark,
}));
};
// state에 업데이트 메서드도 포함되어있으므로
// 이 또한 context Provider를 통해 전달될것입니다.
this.state = {
theme: themes.light,
toggleTheme: this.toggleTheme,
};
}
render() {
// Provider에 state 전체를 넘겨줍니다.
return (
<ThemeContext.Provider value={this.state}>
<Content />
</ThemeContext.Provider>
);
}
}
function Content() {
return (
<div>
<ThemeTogglerButton />
</div>
);
}
ReactDOM.render(<App />, document.root);
각각의 Provider
가 제공하는 value
prop을
각각의 Consumer
가 매개변수로 받는다.
이렇게 설계된 이유는, context
변화로 인해 다시 렌더링하는 과정을 빠르게 유지하기 위함이다.
// 기본값이 light인 ThemeContext
const ThemeContext = React.createContext('light');
// 로그인한 유저 정보를 담는 UserContext
const UserContext = React.createContext({
name: 'Guest',
});
class App extends React.Component {
render() {
const {signedInUser, theme} = this.props;
// context 초기값을 제공하는 App 컴포넌트
return (
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={signedInUser}>
<Layout />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
}
function Layout() {
return (
<div>
<Sidebar />
<Content />
</div>
);
}
// 여러 context의 값을 받는 컴포넌트
function Content() {
return (
<ThemeContext.Consumer>
{theme => (
<UserContext.Consumer>
{user => (
<ProfilePage user={user} theme={theme} />
)}
</UserContext.Consumer>
)}
</ThemeContext.Consumer>
);
}
Provider
에서 내려주는 value
prop이 참조형 타입일 경우, context의 변동유무를 확인하는 알고리즘에 의해서 value
prop이 바뀔때마다 Provider의 하위 컴포넌트들이 리렌더링 되는 문제가 있습니다
따라서, 이를 해결하기 위해서 참조형 타입은 부모의 state로 선언하는 방법이 있습니다
// value가 바뀔때마다 Toolbar가 리렌더링 된다
class App extends React.Component {
render() {
return (
<MyContext.Provider value={{something: 'something'}}>
<Toolbar />
</MyContext.Provider>
);
}
}
// value로 넘겨줄 값을 부모의 state로 빼주자
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: {something: 'something'},
};
}
render() {
return (
<MyContext.Provider value={this.state.value}>
<Toolbar />
</MyContext.Provider>
);
}
}