ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [React]How to optimize context value (번역)
    JavaScript 2022. 3. 3. 22:14

     

    원글: https://kentcdodds.com/blog/how-to-optimize-your-context-value — Kent C. Dodds

    주의: 시작하기 전 알아둘것. 아래 조건에 해당하는 사항이 여러 개 있는 경우 컨텍스트 값 최적화를 효과적으로 도입 할 수 있습니다.

    1. 컨텍스트 값이 자주 바뀔 경우
    2. 컨텍스트 값을 사용하는 소비자 컴포넌트가 많을 경우
    3. `React.memo` 의 느린 성능에 지친 경우
    4. 당신이 프로그램을 실행 했을때 느리다고 생각하여, 최적화가 필요한 경우

    만약 위 설명 중 해당사항이 있으면, 계속 읽으세요 (끝까지 읽고 더 나은 대안을 놓치지 마세요!).
    정말로 대안은 확실히 좋을 겁니다. 심지어 나는 원래 작성했던 블로그 포스트를 삭제하고 다른 더 나은 방법을 알려주기 위해 이 게시물을 다시 작성했습니다. 기존에 작성했던 포스트는 여기서
    진지하게, 만약 당신 코드가 느리다고 생각해서 이짓을 하려고 하는 거면 굳이 안해도 되요. 사실 리액트는 정말 빠르고, 성능이 충분할 때 성능이라는 명목하에 복잡성을 늘리는건 오히려 복잡성을 늘릴 뿐이니깐요.

     

    컨텍스트 값을 최적화 할 수 있는 가장 심플한 방법은 상태 관리를 위해 useReducer 혹은 useState 를 사용하는 것이에요. 그리고 state 를 하나의 특정 함수에 넣고 dispatch 를 다른 함수에 넣으세요. ( 아래 코드의 예시에서 state 는 useCountState 에서, dispatch 는 useCountUpdater 에서 불러옵니다.)

    import React from 'react'
    import ReactDOM from 'react-dom'
    import {CountProvider, useCountState, useCountUpdater} from './count-context'
    
    function useRenderCounter() {
      const ref = React.useRef()
      React.useEffect(() => {
        ref.current.textContent = Number(ref.current.textContent || '0') + 1
      })
      return (
        <span
          style={{
            backgroundColor: '#ccc',
            borderRadius: 4,
            padding: '2px 4px',
            fontSize: '0.8rem',
            margin: '0 6px',
            display: 'inline-block',
          }}
          ref={ref}
        />
      )
    }
    
    const CountDisplay = React.memo(function CountDisplay() {
      const count = useCountState()
      const renderCount = useRenderCounter()
      return (
        <div style={{border: '1px solid black', padding: 10}}>
          {renderCount}
          {`The current count is ${count}. `}
        </div>
      )
    })
    
    const Counter = React.memo(function Counter() {
      const increment = useCountUpdater()
      const renderCount = useRenderCounter()
      return (
        <div style={{border: '1px solid black', padding: 10}}>
          {renderCount}
          <button onClick={increment}>Increment count</button>
        </div>
      )
    })
    
    function App() {
      const [, forceUpdate] = React.useState()
      const renderCount = useRenderCounter()
      return (
        <div style={{border: '1px solid black', padding: 10}}>
          {renderCount}
          <button onClick={() => forceUpdate({})}>force render</button>
          <CountProvider>
            <CountDisplay />
            <Counter />
          </CountProvider>
        </div>
      )
    }
    
    ReactDOM.render(<App />, document.getElementById('root'))

    사실 위 경우에는 useMemo 를 사용할 필요가 없고, 단지 업데이터 컨텍스트(useCountUpdater) 만을 사용해서 리랜더링을 방지 할 수 있습니다.

    <Counter /> 컴포넌트의 컨텍스트가 업데이트 되지 않아서 해당 컴포넌트의 리랜더링을 막을 수 있다는 것을 제외하곤, 이 방법은 기존의 useMemo 를 활용한 해결책과 같습니다. (Counter 컴포넌트에서 useCountState 를 통해 context value 를 구독하고 있지 않기 때문에 dispatch 가 발생해 useCountState 의 value 가 변해도 Counter 컴포넌트는 리랜더링 되지 않습니다.)

    저는 개인적으로 이 방법이 대부분의 상황에 있어서 오히려 더 복잡하게 만든다고 생각하기 때문에 모든 상황에서 굳이 이 방법을 고수하진 않을겁니다. 하지만, 만약에 위에서 언급한 모든 문제를 가지고 있으면 이 방법을 도입해 해결해보는것도 좋은 시도일겁니다.

    State 와 dispatch 를 분리하는 건 귀찮다.

    몇몇 사람들은 State 와 dispatch 로 분리하는 것은 귀찮은 과정이라고 생각합니다.

    const state = useCountState()
    const dispatch = useCountDispatch()

    그들은 그냥 이렇게 하면 안돼? 라고 묻습니다.

    const [state, dispatch] = useCount()

    물론 그렇게 해도 됩니다!!

    function useCount() {
      return [useCountState(), useCountDispatch()]
    }

    하지만 기억해두세요. 이러한 구조를 사용할 때, state 와 dispatch 둘 중 하나만 필요할 경우 성능상 이점을 잃게될 수 있습니다.

    그리고 리액트 컨텍스트를 효과적으로 사용하는 방법 도 꼭 읽어보세요.

Designed by Tistory.