컴포넌트의 props를 통한 데이터 전달
Context를 사용시 곧바로 데이터 전달
기존 방식은 props로 데이터 전달 → props로 데이터 전달 → props로 데이터 전달 → ... (자식 → 자식 → 자식 → ...)
// 상위 App Component
function App(props) {
return <Toolbar theme="dark" />;
}
function Toolbar(props) {
// 이 Toolbar 컴포넌트는 ThemedButton에 theme를 넘겨주기 위해서 'theme' prop을 가져야만 함
// 현재 테마를 알아야 하는 모든 버튼에 대해서 props로 전달하는 것은 굉장히 비효율적
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
function ThemedButton(props) {
return <Button theme={props.theme} />;
}
// 컨텍스트는 데이터를 매번 컴포넌트를 통해 전달할 필요 없이 컴포넌트 트리로 곧바로 전달
// 여기에서는 현재 테마를 위한 컨텍스트를 생성하며 기본값은 'light'
const ThemeContext = React.createContext('light');
// Provider를 사용하여 하위 컴포넌트들에게 현재 테마 데이터를 전달
// 모든 하위 컴포넌트들은 컴포넌트 트리 하단에 얼마나 깊이 있는지에 관계없이 데이터를 읽을 수 있음
// 여기에서는 현재 테마값으로 'dark'를 전달
function App(props) {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
// 이제 중간에 위치한 컴포넌트는 테마 데이터를 하위 컴포넌트로 전달할 필요가 없음
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton(props) {
// 리액트는 가장 가까운 상위 테마 Provider를 찾아서 해당되는 값을 사용
// 만약 해당되는 Provider가 없을 경우 기본값(여기에서는 'light')을 사용
// 여기에서는 상위 Provider가 있기 때문에 현재 테마의 값은 'dark'
return (
<ThemeContext.Consumer>
{value => <Button theme={value} />}
</ThemeContext.Consumer>
);
}
// Page컴포넌트는 PageLayout컴포넌트를 렌더링
<Page user={user} avatarSize={avatarSize} />
// PageLayout컴포넌트는 NavigationBar컴포넌트를 렌더링
<PageLayout user={user} avatarSize={avatarSize} />
// NavigationBar컴포넌트는 Link컴포넌트를 렌더링
<NavigationBar user={user} avatarSize={avatarSize} />
// Link컴포넌트는 Avatar컴포넌트를 렌더링
<Link href={user.permalink}>
<Avatar user={user} size={avatarSize} />
</Link>
// Avatar 최하위컴포넌트, user와 avatarSize가 필요
// 여러 단계를 거쳐 props를 통해 전달
// 하지만 불필요하고 번거로움
Element Variable
function Page(props) {
const user = props.user;
const userLink = (
<Link href={user.permalink}>
<Avatar user={user} size={props.avatarSize} />
</Link>
);
// Page Component는 PageLayout Component를 렌더링
// 이때 props로 userLink를 함께 전달
return <PageLayout userLink={userLink} />;
}
// PageLayout Component는 NavigationBar Component를 렌더링
<PageLayout userLink={...} />
// NavigationBar Component는 props로 전달받은 userLink element를 return
<NavigationBar userLink={...} />
/* 상위 레벨의 Page Component만 Avatar Component에서
필요로 하는 user와 avatarSize만 알고있으면 됨*/
/* 중간 레벨의 Component를 통해 전달해야하는 Props를
없애고 코드를 간결하게 해줌 */
/* 최상위에 있는 Component 많은 권한을 부여 */
// 하위 컴포넌트 의존성을 상위 컴포넌트와 분리
function Page(props) {
const user = props.user;
const topBar = (
<NavigationBar>
<Link href={user.permalink>
<Avatar user={user} size={props.avatarSize} />
</Link>
</NavigationBar>
);
const content = <Feed user={user} />;
return (
<PageLayout
topBar={topBar}
content={content}
/>
);
}
// 하위 컴포넌트가 상위 컴포넌트가 통신해야 할 경우, renderProps를 사용
// 하나의 데이터에 다양한 레벨에 있는 중첩된 Component들이 접근할 필요가 있음 (Context사용)
const MyContext = React.createContext(기본값);
데이터 제공
<MyContext.Provider value={/* some value */}>
// value라는 prop, Provider Component의 하위 컴포넌트에 전달
// 하위 컴포넌트들이 이 값을 사용
// Consuming Component, 값이 변하면 재렌더링
function App(props) {
return (
// value를 직접 넣는 것이 아니고 component state를 옮기고 해당 state 값을 넣어줌
<MyContext.Provider value={{ something: 'something' }}>
<Toolbar />
</MyContext.Provider>
);
}
function App(prosp) {
const [value, setValue] = useState({ something: 'something' });
return (
<MyContext.Provider value={value}>
<Toolbar />
</MyContext.Provider>
);
}
class MyClass extends React.Component {
componentDidMount() {
let value = this.context;
/* MyContext의 값을 이용하여 원하는 작업을 수행 가능 */
}
componentDidUpdate() {
let value = this.context;
/* ... */
}
componentWillnmount() {
let value = this.context;
/* ... */
}
render() {
let value = this.context;
/* MyContext의 값에 따라서 컴포넌트들을 렌더링 */
}
}
MyClass.contextType = MyContext;
// MyClass라는 Class Component는 MyContext 데이터에 접근
// Class Component에 있는 Context Type 속성에는 React.createContext 함수를 통해 생성된 context 객체가 대입될 수 있음
// this.context를 통해 상위에 있는 provider 중에서 가장 가까운 값을 가져올 수 있음
Context 데이터를 구독
<MyContext.Consumer>
{value => /* 컨텍스트의 값에 따라서 컴포넌트들을 렌더링 */}
</MyContext.Consumer>
/* Function as a child
컴포넌트 자식으로 함수가 올 수 있음 */
/* Context.Consumer로 감싸주면
자식으로 들어간 함수가 현재 Context value를 받아 React node로 return*/
/* 이 때 함수로 전달되는 value는 provider의 value prop과 동일 */
/* 만약 상위 컴포넌트의 provider가 없다면 이 value parmeter는 createcontext로 호출하는 기본값과 동일 */
// children이라는 prop을 직접 선언하는 방식
<Profile children={name => <p>이름: {name}</p>} />
// Profile컴포넌트를 감싸서 children으로 만드는 방식
<Profile>{name => <p>이름: {name}</p>}</Profile>
문자열 속성
const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';
// 개발자 도구에 "MyDisplayName.Provider"로 표시
<MyContext.Provider>
// 개발자 도구에 "MyDisplayName.Consumer"로 푯;
<MyContext.Consumer>
function MyComponent(props) {
const value = useContext(MyContext);
return (
...
)
}
react.createContext 함수 호출로 생성된 context 객체를 인자로 받아서 현재 context 값을 리턴
useContext Hook을 사용하면
만약 context값이 변경되면 변경된 값과 함께 useContext hook을 사용하는 컴포넌트가 재렌더링
useContext hook을 사용하는 컴포넌트 렌더링이 무거운 작업일 경우
파라미터로 context 객체를 넣어줘야함
// 올바른 사용법
useContext(MyContext);
// 잘못된 사용법
useContext(MyContext.Consumer);
useContext(MyContext.Provider);