Skip to content

[학습] Context API를 이해하자!

Jeongsoo Shin edited this page Nov 22, 2019 · 2 revisions

Context API

목적

  1. React component tree에서 global하게 데이터를 공유할 수 있는 방법
  2. 예컨대 전체 Theme을 하위 컴포넌트들에서 사용한다던지...
  3. 전역변수를 사용할 수도 있고 / 구독할 수도 있다.
        // context를 사용하면 모든 컴포넌트를 일일이 통하지 않고도
        // 원하는 값을 컴포넌트 트리 깊숙한 곳까지 보낼 수 있습니다.
        // light를 기본값으로 하는 테마 context를 만들어 봅시다.
        const ThemeContext = React.createContext('light');
        
        class App extends React.Component {
          render() {
            // Provider를 이용해 하위 트리에 테마 값을 보내줍니다.
            // 아무리 깊숙히 있어도, 모든 컴포넌트가 이 값을 읽을 수 있습니다.
            // 아래 예시에서는 dark를 현재 선택된 테마 값으로 보내고 있습니다.
            return (
              <ThemeContext.Provider value="dark">
                <Toolbar />
              </ThemeContext.Provider>
            );
          }
        }
        
        // 이젠 중간에 있는 컴포넌트가 일일이 테마를 넘겨줄 필요가 없습니다.
        function Toolbar(props) {
          return (
            <div>
              <ThemedButton />
            </div>
          );
        }
        
        class ThemedButton extends React.Component {
          // 현재 선택된 테마 값을 읽기 위해 contextType을 지정합니다.
          // React는 가장 가까이 있는 테마 Provider를 찾아 그 값을 사용할 것입니다.
          // 이 예시에서 현재 선택된 테마는 dark입니다.
          static contextType = ThemeContext;
          render() {
            return <Button theme={this.context} />;
          }
        }

React.createContext

  • Context 객체를 구독하고 있는 컴포넌트를 렌더링할 때, React는 트리 상위에서 가장 가까이 있는 짝이 맞는 Provider로부터 현재값을 읽음

  • defaultValue 매개변수는 트리 안에서 적절한 Provider를 찾지 못햇을 때에만 쓰이는 값

    → 컴포넌트를 독립적으로 테스트할 때 유용

  • Provider를 통해 undefined을 값으로 보낸다고 해도 구독 컴포넌트들이 defaultValue 를 읽지는 않는다

React.useContext

  1. render props를 사용하는 것보다 훨씬 편함
  2. 사용방법
    1. [Context정의] createContext로 context객체 생성
            // StayContext.js
            import React, { createContext } from 'react';
            const StayContext = createContext();
2. [Context정의] context객체의 Provider를 이용해서 props.children을 렌더링하는 provider역할 객체 생성

    → render props를 사용할 때와 달리, Consumer는 export하지 않아도 된다.
            // StayContext.js
            function StayProvider(props) {
              const value = {
                ...
              };
              const { children } = props;
              return (
                <StayContext.Provider value={value}>
                  {children}
                </StayContext.Provider>
              );
            }
            
            export { StayContext, StayProvider }
3. [context 제공범위설정] context를 사용할 범위를 Provider로 감싸준다.
            // 최상단 컴포넌트 App.js에서 설정한 경우
            function App() {
              return (
                <ThemeProvider theme={theme}>
                  <OpacityProvider>
                    <StayProvider>
                      <GlobalStyle />
                      <FilterBar />
                      <StayContainer />
                    </StayProvider>
                  </OpacityProvider>
                </ThemeProvider>
              );
            }
4. [context 사용] context를 불러와 value에서 필요한 객체를 불러와 사용한다.
            // PriceModal.js
            import React, { useContext } from 'react';
            import { StayContext } from '../Context/StayContext';
            
            function PriceModal() {
              const { addFilter } = useContext(StayContext);
            
            	return (
                    <button onClick={() => { addFilter(); } />
            	  );
            }

[ContextObjectName].Provider

    <MyContext.Provider value={/* 어떤 값 */}>
  • Context 오브젝트에 포함된 React 컴포넌트인 Provider

    → context를 구독하는 컴포넌트들에게 context의 변화를 알리는 역할

  • Provider 하위에 또 다른 Provider를 배치할 경우, 하위의 값이 우선

  • 객체를 value로 보내는 경우 필요없는 렌더링이 생길 수 있다.

    → state를 부모로 끌어올려 해결하라!

Provider 하위에서 context를 구독하는 모든 컴포넌트는, Provider의 value prop이 바뀔 때마다 다시 렌더링됩니다.

대안~합성

  1. 컴포넌트에 다른 컴포넌트를 담는게 Context API를 사용하는 것보다 훨씬 좋을 수 있다.
    • Page에 userLink를 정의하고 자식 컴포넌트들에 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. pros.children
    • 상위 컴포넌트에서 하위 컴포넌트 내에 다른 element를 삽입하여 렌더링하는 경우
    • 하위 컴포넌트에서 props.children를 이용하여 그의 하위 컴포넌트에 또 전달할 수 있다.
    • 어떤 자식 엘리먼트가 들어올 지 예상할 수 없을 때 사용!
Clone this wiki locally