useContext
const value = useContext(MyContext);
React.createContext 를 통해 생성된 context 객체를 받아 context의 현재 값을 반환한다. 
- 가장 가까운 
<MyContext.Provider> 가 갱신되면 가장 최신의 context value를 사용해 렌더러를 트리거한다. 상위에서 React.memo 혹은 shouldComponentUpdate를 사용하더라도 useContext를 사용하고 있는 컴포넌트 자체에서부터 다시 렌더링된다. 
- Context API는 상태를 전역적으로 공유해주는 기능만을 수행한다.
 
value가 객체로 되어있을 경우 하나라도 바뀌면 전체가 리렌더링된다. 
- context 값의 변화는 Object.is()와 동일한 알고리즘으로 비교한다.
 
useContext 원리
function createEventEmitter(value) {
  let handlers = []
  return {
    on(handler) {
      handlers.push(handler)
    },
    off(handler) {
      handlers = handlers.filter(h => h !== handler)
    },
    get() {
      return value
    },
    set(newValue) {
      value = newValue
      handlers.forEach(handler => handler(value))
    },
  }
}
- 이벤트를 발행한뒤 이를 구독하고 있는 객체에게 통지하는 역할을 이벤트 에미터로 구현할 수 있다.
 
- 이 경우에는 이벤트 에미터가 갖고 있을 메시지, 즉 value를 인자로 받고 on을 통해 handlers 배열에 구독자를 등록한다. get, set 메소드를 통해 메시지를 조회하고 새로운 메시지를 받아 구독자들에게 통지할 수 있다.
 
const MyReact = (() => {
  function createContext(initialValue) {
    const emitter = createEventEmitter(initialValue)
		const Provider = ({ value, children }) => {
		  // value 값이 변하면 이벤트 에미터에게 이를 알린다.
		  // 이벤트 에미터는 구독하고 있는 객체들에게 이를 전파할 것이다.
		  React.useEffect(() => {
		    emitter.set(value)
		  }, [value])
		  return <>{children}</>
		}
		const Consumer = ({ children }) => {
		  // 리렌더링을 위해 이벤트 에미터의 값을 상태 value로 가지고 있다.
		  const [value, setValue] = useState(emitter.get())
		
		  // 이벤트 에미터를 구독한다.
		  // 이벤트 에미터가 변경을 알리면 이 값을 상태로 세팅한다.
		  // 상태가 변경되면 리액트는 이 컴포넌트를 다시 그릴 것이다.
		  React.useEffect(() => {
		    emitter.on(setValue)
		    return () => emitter.off(setValue)
		  }, [])
		  return <>{children(value)}</>
		}
    return {
      Provider,
      Consumer,
			emitter,
    }
  }
	// 컨택스트 값을 사용할 수 있는 훅이다.
  function useContext(context) {
    // 컨택스트 값을 상태 value로 저장해 둔다
    const [value, setValue] = useState(context.emitter.get())
    React.useEffect(() => {
      // 컨택스트의 이벤트 에미터로부터 값을 수신하면 상태 value를 갱신다.
      // 이 상태를 사용하는 컴포넌트는 리렌더링 될 것이다.
      context.emitter.on(setValue)
      return () => context.emitter.off(setValue)
    }, [context])
    // 컨택스트 값을 반환한다.
    return value
  }
  return {
    createContext,
    // 훅을 제공한다.
    useContext,
  }
})()
const PlusButton = () => {
  return (
    <countContext.Consumer>
      {({ count, setCount }) => (
        <button onClick={() => setCount(count + 1)}>+ 카운트 올리기</button>)}
    </countContext.Consumer>)
}