[+73] React context (Redux랑 비슷)

AeRi Lee·2020년 4월 9일
0

Context

Redux없이도 state관리를 해줄 수 있는데 그것이 context이다.
한편으로, Redux처럼 React 컴포넌트 트리 안에서 global하게 데이터를 공유할 수 있다.

주의

context의 주된 용도는 다양한 레벨에 nesting된 많은 컴포넌트에게 data를 전달하는 것이고 context를 사용하면 component를 재사용하기 어려워지므로 꼭 필요할 때만 써야 한다.

API

  1. React.createContext
  2. Context.Provider
  3. Class.contextType
  4. Context.Consumer
  5. Context.displayName

1.React.createContext

사용법

const MyContext = React.createContext(defaultValue);

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

DefaultValue 매개변수는 트리 안에서 적절한 Provider를 찾지 못했을 때만 쓰이는 값이다. 컴포넌트를 독립적으로 테스트 할 때 유용한 값이기도 하다.

2.Context.Provider

사용법

<MyContext.Provider value={어떤 값}>

Context 객체에 포함된 Provider는(=React 컴포넌트) context를 구독하는 컴포넌트들에게 context의 변화를 알리는 역할을 한다.

Provider는 value prop을 받아서 이 값을 하위에 있는 컴포넌트에게 전달한다. 여기서 값을 전달받을 수 있는 컴포넌트의 수에 제한은 없다. Provider 하위에 또 다른 Provider를 배치하는 것도 가능하며, 이렇게 되면 하위에 있는 Provider의 값이 우선시된다.

Provider 하위에서 context를 구독하는 모든 컴포넌트는 Provider의 value prop가 바뀔 때 마다 다시 렌더링 된다. Provider로부터 하위 consumer (.contextTypeuseContext 포함)로 전파할 때 shouldComponentUpdate 메서드가 적용되지 않으므로, 상위 컴포넌트가 업데이트를 건너뛰더라도 consumer가 업데이트 된다.

context의 값이 바뀌었는지 여부는 Object.is와 동일한 알고리즘을 사용해 이전 값과 새로운 값을 비교하면 된다.

잠시..Object.is()메서드

Object.is() 메서드는 두 값이 값은 값인 지 결정한다.
Object.is(value1, value2) => 결과 Boolean.

주의사항

다시 렌더링할지 여부를 정할 때 잠조를 확인하기 때문에 Provider의 부모가 렌더링 될 때마다 불 필요하게 하위 컴포넌트가 다시 렌더링 되는 문제가 생길 수도 있다. 아래 코드에서 value가 바뀔 때마다 매번 새로운 객체가 생성되므로 Provider가 렌더링 될 때마다 하위에서 구독하고 있는 컴포넌트 모두가 다시 렌더링 될 것이다.

const App extends React.Component {
  render () {
    return (
      <MyContext.Provider value={{something: 'something'}}>
        <Toolbar />
      </MyContext.Provider>
    )
  }
}

이를 피하기 위해서 값을 부모의 state로 끌어올리자.

const App extends React.Component {
   constructor(props) {
     super(props);
     this.state = {
       value: {something: 'something'},
     }; 
   }
   
   render(){
     return(
       <Provider value={this.state.value}>
         <Toolbar />
       </Provider>
     )
   }
}

3.Class.contextType

context 한개 구독하기

class MyClass extends React.Component {
  componentDidMount () {
    let value = this.context;
    MyContext의 값을 이용한 코드
  }
  
  componenDIdUpdate(){
    let value = this.context;
    //
  }
  
  componentWillUnmount () {
    let value = this.context
    //
  }
  
  render (){
    let value = this.context;
    //
  }
}

MyClass.contextType = MyContext;

context 여러개 구독하기

const ThemeContext = React.createContext('light');
const UserContext = React.createContext({
  name: 'Guest',
})

class App extends React.Component {
  render(){
    const {signedInUser, theme} = this.props;
    
    return(
      <ThemeContext.Provider value={theme}>
        <UserContext.Provider value={signedInUser}>
          <Layout />
        </UserContext.Provider>
      </ThemeContext.Provider>
    )
  }
}

function Layout(){
  return(
    <div>
      <Sidebar />
      <Content />
    </div>
  )
}

//여러 context의 값을 받는 컴포넌트
function Content() {
  return(
    <ThemeContext.Consumer>
      {theme => (
        <UserContext.Consumer>
          {user => (
            <ProfilePage user={user} theme={theme} />
          )}
        </UserContext.Consumer>
      )}
    </ThemeContext.Consumer>
  )
}

이렇게 둘 이상의 context 값이 함꼐 쓰이는 경우가 많다면 그 값들을 한 번에 받는 render prop 컴포넌트를 만드는 것도 고려해보자.

설명

Context객체를 원하는 Class의 contextType 프로퍼티로 지정할 수 있다. 그러면 그 Class안에서 this.context를 이용해 해당 Context의 가장 가까운 Provider를 찾아 그 값을 읽을 수 있게 된다. 이 값은 render를 포함한 모든 lifecycle 메서드에서 사용할 수 있다.


4.Context.Consumer

사용법

<MyContext.Consumer>
  {value => context 값을 이용한 렌더링 }
</MyContext.Consumer>

설명
context 변화를 구독하는 React컴포넌트이다.
Context.Consumer의 자식은 함수 여야 하고 함수 컴포넌트 안에서 context를 읽기 위해서 쓸 수 있다. 이 함수를 context의 현재값을 받고 React node값을 반환한다. 이 함수가 받는 value 또한 가장 가까운 상위 Provider의 value prop과 같다. 상위 Provider가 없다면 당연히....defaultValue와 동일할 것이다.


5.Context.displayName

Context 객체는 displayName 문자열 속성을 설정할 수 있다. React 개발자 도구는 이 문자열을 사용해서 context를 어떻게 보여줄 지 결정한다.

예를 들어 아래 컴포넌트는 개발자 도구에 MyDisplayName으로 표시된다.

const MyContext = React.createContext(어떤 value);
MyContext.displayName = 'MyDisplayNmae';

<MyContext.Provider>//MyDisplayName.Provider 라고 뜬다.
<MyContext.Consumer>//MyDisplayName.Consumer 라고 뜬다.
profile
👩🏻‍💻 Junior Web Frontend Developer

0개의 댓글