Web/React

How does it React Hook re-rendering

레에몽 2021. 4. 24. 12:14

medium을 요약하려고 한다. 문제가 되면 삭제하겠다.

  • 결론적으로 React Hook은 Class Component가 Re-rendering 해주는 과정과 같다.

먼저 Class 형태의 Re-rendering 과정을 살펴보자.

  • // extreamly simplified implementation
    OverReact = (function () {
      function render(Component) {
        const instance = new Component()
        instance.render()
        return instance
      }
      class Component {
        constructor(props) {
          this.props = props
        }
        setState(state) {
          this.state = state
          this.render()
        }
      }
      return {
        render,
        Component,
      }
    })()
    OverReact에서 render는 Component 객체를 만들어서 render를 시키고 return 해준다.
    • 이때, instance.render가 실행해주는 것은 instance의 render 메소드이다.
  • this.setState에서 rendering을 트리거 해준다.
  • 다음은 예시 코드이다.
  • OverReact = (function () {
      function render(Component) {
        const instance = new Component()
        instance.render()
        return instance
      }
      class Component {
        constructor(props) {
          this.props = props
        }
        setState(state) {
          this.state = state
          this.render()
        }
      }
      return {
        render,
        Component,
      }
    })()
    const { render, Component } = OverReact
    class ExtendedComponent extends Component {
      constructor(props) {
        super(props)
        this.state = {
          counter: 0,
          name: "foo",
        }
      }
    
      plusOne() {
        const { state: previousState } = this
        let { counter } = previousState
        counter = counter + 1
        this.setState(Object.assign(previousState, { counter }))
      }
      updateName(name) {
        const { state: previousState } = this
        this.setState(Object.assign(previousState, { name }))
      }
    
      render() {
        const { counter, name } = this.state
        console.log(`rendered, counter: ${counter}, name: ${name}`)
      }
    }
    
    // instance -> return OverReact.render method
    const instance = render(ExtendedComponent)
    // rendered, counter: 0, name: foo
    instance.plusOne()
    // rendered, counter: 1, name: foo
    instance.updateName("bar")
    // rendered, counter: 1, name: bar
    instance.plusOne()
    // rendered, counter: 2, name: bar
    instance.updateName("baz")
    // rendered, counter: 2, name: baz
    instance.plusOne()
    // rendered, counter: 3, name: baz
    setState가 instance에 액세스가 되므로, Component를 rendering을 다시 할 수 있음을 알 수 있다.

Hook에서는 어떻게 될까?

  • // extreamly simplified implementation
    function render(Component) {
      // ToDo
      // render the Component
    }
    function useState(initialState) {
      // ToDo
      // derive currentState
      let currentState
    
      function setState(newState) {
        // ToDo
        // update currentState with newState
        // rerender the component
      }
      return [currentState, setState]
    }
    OverReact = {
      render,
      useState,
    }
    크게 달라질 부분이 없다.
  • setState 부분이 Component 함수에 접근할 수 있어야 하므로 OverReact의 render 함수를 trigger해주는 형태로 만들면 된다.
  • useState가 여러개 호출되는 경우가 많기에 callId를 사용해 줄 것이다.
    • 자세한 내용은 medium에서 확인이 가능하나 나는 제대로 이해하지 못했다.
  • 다음은 예시 코드이다.
OverReact = (function () {
  let context = {}
  let callId = -1
  function render(Component) {
    context.Component = Component
    const instance = Component()
    instance.render()
    callId = -1
    delete instance.render
    context.instance = instance
    return context
  }
  function useState(initialState) {
    if (!context) {
      throw new Error("hooks can not be called with out a rendering context")
    }
    if (!context.hooks) {
      context.hooks = []
    }

    callId = callId + 1

    const hooks = context.hooks
    const currentState = hooks[callId] ? hooks[callId] : initialState
    hooks[callId] = currentState
    const setState = (function () {
      const currentCallId = callId
      return function (newState) {
        hooks[currentCallId] = newState
        render(context.Component)
      }
    })()

    return [currentState, setState]
  }
  return {
    render,
    useState,
  }
})()
const { render, useState } = OverReact
function Component() {
  const [counter, setCounter] = useState(0)
  const [name, setName] = useState("foo")

  function plusOne() {
    setCounter(counter + 1)
  }

  function updateName(name) {
    setName(name)
  }
  function render() {
    console.log(`rendered, counter: ${counter}, name: ${name}`)
  }

  return {
    render,
    plusOne,
    updateName,
  }
}

const context = render(Component)
// rendered, counter: 0, name: foo
context.instance.plusOne()
// rendered, counter: 1, name: foo
context.instance.updateName("bar")
// rendered, counter: 1, name: bar
context.instance.plusOne()
// rendered, counter: 2, name: bar
context.instance.updateName("baz")
// rendered, counter: 2, name: baz
context.instance.plusOne()
// rendered, counter: 3, name: baz
  • react에 있는 useState는 setState에서 OverReact의 render를 호출해준다.
  • 그리고 OverReact는 Componenet의 Render를 호출해주면서 Re-Rendering이 된다.