context-api

2018-12-17

Context API는 props를 직접 전달하는 것과 바로 상태 전달이 가능하다.
store를 두고서 상태값들을 서로 공유할 수 있다.
기존의 redux 보다는 사용방법이 훨씬 간단하다.

우선 store공간을 만든다.(store.js)
context는 createContext라는 함수를 통해 만든다.
이 함수를 호출하면 Provider와 Consumer라는 컴포넌트들이 반환된다.

1
2
3
4

import React from "react";
const Store = React.createContext(null);
export default Store;

Context를 프로젝트에 적용하려면 최상단을 Provider로 감싸야한다.
Provider 내에서 사용할 값은 “value”라고 부른다.
클래스가 생성되었을 때 store가 value를 얻게 된다.
그래서 Provider에 사용할 함수는 반드시 생성자에 명시해야한다.
생성자에서 state 값 뿐만 아니라 함수도 선언해줘야한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import React, { Component } from "react";
import AppPresenter from "./AppPresenter";
import Store from "store";

class AppContainer extends Component {
constructor() {
super();
this._changeMessage = () => {
if (this.state.message === 'hello') {
this.setState({
message: 'bye bye',
})
} else {
this.setState({
message: 'hello',
})
}
}
this.state = {
message: 'hi',
changeMessage: this.__changeMessage,
}
}

render(){
return(
<Store.Provider value={this.state}>
<AppPresenter/>
</Store.Provider>
)
}
}

export default AppContainer;

Consumer는 Context를 사용해야할 때 사용한다.
Consumer는 함수가 아닌 다른 child를 사용하면 안된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React, { Fragment } from "react";
import Notification from "Components/Notification";
import Store from 'store';

const AppPresenter = () => (
<Fragment>
<Store.Consumer>
{store => (<Notification
key={store.notification[key].id}
id={store.notification[key].id}
text={store.notification[key].text}
seen={store.notification[key].seen}
onClick={store.changeMessage}
/>
)
}
}
</Store.Consumer>
</Fragment>
);
export default AppPresenter;

context가 여러개 있을 때 Provider를 각각 생성해서 최상위에 여러겹 쌓는게 마음에 걸린다.
자바스크립트 내장함수인 reduce를 사용하여 깔끔하게 정리할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React from 'react';
import { SampleProvider } from './contexts/sample';
import { AnotherProvider } from "./contexts/another";

const AppProvider = ({contexts, children}) => contexts.reduce(
(prev, context) => React.createElement(context, {
children: prev
}),
children
);

const App = () => {
return (
<AppProvider
contexts={[SampleProvider, AnotherProvider]}
>
</AppProvider>
);
};

export default App;

또한 Consumer에 사용되는 로직을 쉽게 재사용할 수 있도록 HoC를 사용한다면 매우 편리해진다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import React, { Component, createContext } from 'react';

const Context = createContext();

const{ Provider, Consumer } = Context;

//HoC사용
function useSample(WrappedComponent) {
return function UseSample(props) {
return (
<Consumer>
{
({state, actions}) => (
<WrappedComponent
value={state.value}
setValue={actions.setValue}
/>
)
}
</Consumer>
)
}
}

위의 코드를 필요한 하위 컴포넌트에서 가져다 쓰면 된다!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14

import React from 'react';
import {useSample} from "../contexts/sample";

const Receives = ({value}) => {
return (
<div>
현재 설정된 값: {value}
</div>

);
};

export default useSample(Receives);

redux에 비해 훨씬 더 간단하긴 하지만 redux는 단순힌 전역 상태 관리 그이상의 가치가 있다고 한다.
액션기반 앱 상태 업데이트 로직 작성부터, 미들웨어, 강력한 개발자도구까지…
Suspense API 또한 요즘 대세라고 한다.
기존에는 리액트는 데이터가 있을 때까지 항상 로딩상태를 가지지만 Suspense API는 데이터가 있을 때까지 렌더링 되지 않는다고 한다.