방송함// before
class App extends React.Component {
render() {
return <Toolbar theme="dark" />;
}
}
function Toolbar(props) {
return (
<div>
<ThemedButton theme={props.theme} />
</div>
)
}
class ThemedButton extends React.Component {
render() {
return <Button theme={this.props.theme} />;
}
}
// after
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
return (
<ThemeContext.Provider>
<Toolbar theme="dark" />
</ThemeContext.Provider>
);
}
}
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
)
}
class ThemedButton extends React.Component {
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
컴포넌트 합성이 더 좋은 방법임// before
<Page user={user} avatarSize={avatarSize} />
// ... 그 아래에 ...
<PageLayout user={user} avatarSize={avatarSize} />
// ... 그 아래에 ...
<NavigationBar user={user} avatarSize={avatarSize} />
// ... 그 아래에 ...
<Link href={user.permalink}>
<Avatar user={user} size={avatarSize} />
</Link>
context를 사용하지 않고 Link와 Avartar 컴포넌트 자체를 넘겨줘서 해결할 수 있음// after
function Page(props) {
const user = props.user;
const userLink = (
<Link href={user.permalink}>
<Avartar user={user} size={user.avartarSize} />
</Link>
);
return <PageLayout userLink={userLink} />;
}
<Page user={user} avatarSize={avatarSize} />
// ... 그 아래에 ...
<PageLayout userLink={...} />
// ... 그 아래에 ...
<NavigationBar userLink={...} />
// ... 그 아래에 ...
{props.userLink}
이렇게 바꾸면 Linkdhk Avartar 컴포넌트가 ink와 AvatarSize를 쓴다는 것을 알고 있는 것은 page 컴포넌트뿐임 : 제어의 역전(inverse of control)
자식으로 둘 수 있는 컴포넌트의 개수는 제한 없음
function Page(props) {
const user = props.user;
const context = <Feed user={user} />;
const topBar = (
<NavigationBar>
<Link href={user.permalink}>
<Avatar user={user} size={props.avatarSize} />
</Link>
</NavigationBar>
);
return (
<PageLayout
topBar={topBar}
content={content}
/>
);
}
render props를 이용하면 렌더링 되기 전부터 자식 컴포넌트가 부모 컴포넌트와 소통하게 할 수 있음const MyContext = React.createContext(defaultValue);
Context 객체를 생성하는 API
Provider로부터 현재 context 값을 읽음defaultValue 매개변수 : 트리 안에서 적절한 Provider를 찾지 못했을 때만 쓰이는 값
defaultValue를 사용하지 않음<MyContext.Provider value={/* 어떤 값 */}>
Context 객체마다 Provider 컴포넌트가 함께 함
Provider 컴포넌트는 구독 컴포넌트에 context의 변화를 알리는 역할을 함
Provider는 value props를 받아서 이 값을 하위에 있는 구독 컴포넌트에게 전달함
Provider은 여러 구독 컴포넌트와 연결될 수 있음
Provider 하위에 또 다른 Provider를 배치할 수 있음
Provider 하위에서 context를 구독하는 모든 컴포넌트는 Provider의 value props가 바뀔 때마다 리렌더링됨
Provider로부터 하위 consumer로의 전파는 shouldComponentUpdate 메서드가 적용되지 않으므로, 상위 컴포넌트가 업데이트를 건더 뛰더라도 consumer가 업데이트됨
.contextType과 useContext를 포함한 컴포넌트constext 값이 바뀌었는지의 여부는 Object.is와 동일한 알고리즘을 사용하여 이전 값과 새로운 값을 비교해서 측정함
value로 보내는 경우 문제가 생길 수 있음class App extends React.Component {
render() {
return (
<MyContext.Provider value={{something: 'something'}}>
<Toolbar />
</MyContext.Provider>
)
}
}
value가 바뀔 때마다 매번 새로운 객체가 생성되므로 Provider가 렌더링될 때마다 하위에서 구독하고 있는 모든 컴포넌트가 리렌더링됨class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: {something: 'something'},
};
}
render() {
return (
<MyContext.Provider value={{something: 'something'}}>
<Toolbar />
</MyContext.Provider>
)
}
}
리렌더링 여부를 정할 때 참조(reference)를 확인하기 때문에, Provider의 부모 컴포넌트가 렌더링될 때마다 불필요하게 하위 컴포넌트가 리렌더링되는 문제가 발생할 수 있음