useContext

const value = useContext(MyContext);

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))
    },
  }
}
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>)
}