방송
함// 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의 부모 컴포넌트가 렌더링될 때마다 불필요하게 하위 컴포넌트가 리렌더링되는 문제가 발생할 수 있음