[React] Context Module Function 패턴

2023. 2. 2. 15:59·React/실험실
반응형
// src/context/counter.js
const CounterContext = React.createContext()

function CounterProvider({step = 1, initialCount = 0, ...props}) {
  const [state, dispatch] = React.useReducer(
    (state, action) => {
      const change = action.step ?? step
      switch (action.type) {
        case 'increment': {
          return {...state, count: state.count + change}
        }
        case 'decrement': {
          return {...state, count: state.count - change}
        }
        default: {
          throw new Error(`Unhandled action type: ${action.type}`)
        }
      }
    },
    {count: initialCount},
  )

  const value = [state, dispatch]
  return <CounterContext.Provider value={value} {...props} />
}

function useCounter() {
  const context = React.useContext(CounterContext)
  if (context === undefined) {
    throw new Error(`useCounter must be used within a CounterProvider`)
  }
  return context
}

export {CounterProvider, useCounter}
// src/screens/counter.js
import {useCounter} from 'context/counter'

function Counter() {
  const [state, dispatch] = useCounter()
  const increment = () => dispatch({type: 'increment'})
  const decrement = () => dispatch({type: 'decrement'})
  return (
    <div>
      <div>Current Count: {state.count}</div>
      <button onClick={decrement}>-</button>
      <button onClick={increment}>+</button>
    </div>
  )
}
// src/index.js
import {CounterProvider} from 'context/counter'
import Counter from 'screens/counter'

function App() {
  return (
    <CounterProvider>
      <Counter />
    </CounterProvider>
  )
}

 

시작부터 긴 코드 덩어리가 나와서 읽기 거북할텐데, 간단하게 정리하면 Context를 사용해서 간단한 Counter를 만들었다. 

Context에서 useReducer를 사용해서 action에따라 +1, -1을 하고 있다. 

 

그런데 Counter 컴포넌트에서 사용하고 있는 Reducer에 집중해보자 

function Counter() {
  const [state, dispatch] = useCounter()
  const increment = () => dispatch({type: 'increment'})
  const decrement = () => dispatch({type: 'decrement'})
  return (
    <div>
      <div>Current Count: {state.count}</div>
      <button onClick={decrement}>-</button>
      <button onClick={increment}>+</button>
    </div>
  )
}

increment 와 decrement는 dispatch를 호출해서 만들어지고 있는것이 보인다. 

 

생각해보면 이것은 썩 유쾌한 작업이 아니다.

const change = action.step ?? step
switch (action.type) {
  case 'increment': {
    return {...state, count: state.count + change}
  }
  case 'decrement': {
    return {...state, count: state.count - change}
  }
  default: {
    throw new Error(`Unhandled action type: ${action.type}`)
  }
}

이미 Context의 useReducer에서 increment와 decrement에 대한 작업을 모두 선언했는데, 실제로 사용하기 위해서 

dispatch를 호출하는 함수를 매번 만들어주는 작업은 생각보다 많이 귀찮은 작업이다. 

 

어떤 방법을 사용하면 이 작업을 쉽게 해결할 수 있을까? 

 

"Helper" 함수를 만들고 Context를 포함시키기

function CounterProvider({step = 1, initialCount = 0, ...props}) {
  const [state, dispatch] = React.useReducer(
    // ...
  )
  
  const increment = React.useCallback(
    () => dispatch({type: 'increment'}),
    [dispatch],
  )
  const decrement = React.useCallback(
    () => dispatch({type: 'decrement'}),
    [dispatch],
  )

  const value = {state, increment, decrement}
  return <CounterContext.Provider value={value} {...props} />
}

useCallback 의 의존성 배열에 dispatch를 추가하고 increment와 decrement를 만들어서 제공해주는 방식이다. 

 

Dispatch를 사용하기

Helper 함수를 만드는 방식도 나쁜 방식은 아니다. 하지만 Dan이라는 사람은 다음과 같이 말했다. 

Helper 메서드는 쓰레기 객체이다. 
보기에는 좋은 구문일지 몰라도 특별한 목적없이 다시 만들고 비교해야 한다. 

개인적으로는 워딩이 쌔다고 느끼지만 결국 Dan이라는 사람이 하고 싶은 말은 Helper 메서드를 사용하기 보다는 

원래 가지고 있는 Dispatch를 사용하는 것을 권장하는 것이다. 

 

function CounterProvider({step = 1, initialCount = 0, ...props}) {
  // ...
  
  return <CounterContext.Provider value={value} {...props} />
}

function useCounter() {
  // ...
}

const increment = dispatch => dispatch({type: 'increment'})
const decrement = dispatch => dispatch({type: 'decrement'})

export {CounterProvider, useCounter, increment, decrement}

함수를 useCallback을 사용하지 않고 dispatch를 파라미터로 제공하는 함수를 export 한다. 

 

다음과 같이 작업하면 Helper 메서드를 만들었을 때처럼 코드의 중복을 줄일 수 있고,

useCallback의 메모이제이션의 의존성 배열의 실수를 방지하고 성능 이슈를 피할 수 있다. 

 

import {useCounter, increment, decrement} from 'context/counter'

function Counter() {
  const [state, dispatch] = useCounter()
  return (
    <div>
      <div>Current Count: {state.count}</div>
      <button onClick={() => decrement(dispatch)}>-</button>
      <button onClick={() => increment(dispatch)}>+</button>
    </div>
  )
}

Redux 등을 사용하지 않고 Context를 사용하는 경우 도움이 될 것 같다. 

 

출처 

https://advanced-react-patterns.netlify.app/1

 

Advanced React Patterns 🤯

 

advanced-react-patterns.netlify.app

 

반응형
저작자표시 비영리 변경금지

'React > 실험실' 카테고리의 다른 글

[React] redux-persist  (1) 2023.02.11
[React] Compound Component 패턴  (1) 2023.02.04
[React] Context API 언제 사용해야할까?  (0) 2023.01.31
[React] 독립된 React 컴포넌트의 이점  (0) 2023.01.29
[React] Suspense  (0) 2023.01.28
'React/실험실' 카테고리의 다른 글
  • [React] redux-persist
  • [React] Compound Component 패턴
  • [React] Context API 언제 사용해야할까?
  • [React] 독립된 React 컴포넌트의 이점
잉여개발자
잉여개발자
풀스택 개발자를 목표로 잉여롭게 개발 공부도 하면서 다양한 취미 생활도 즐기고 있는 잉여 개발자입니다.
  • 잉여개발자
    잉여로운 개발일지
    잉여개발자
    • 분류 전체보기 (789)
      • 개발정보 (36)
      • 개발환경 (7)
      • 개발생활 (19)
      • React (141)
        • 이론 (23)
        • 기능 (12)
        • 실험실 (88)
        • 버그 (6)
        • 패스트캠퍼스 (9)
        • Npm (3)
      • React Native (28)
        • 공통 (6)
        • TypeScript (3)
        • JavaScript (18)
        • 버그 (1)
      • Next.js (30)
        • 이론 (13)
        • 실험실 (13)
        • 버그 (3)
      • Web (35)
      • 알고리즘 (202)
        • 풀이 힌트 (39)
      • JavaScript (47)
      • TypeScript (29)
        • 기초 (27)
        • 실험실 (2)
      • Node.js (13)
        • 이론 (0)
        • 기능 (3)
        • 실험실 (9)
        • 버그 (1)
      • 도커 (4)
      • CCNA (22)
        • 이론 (4)
        • 문제 (18)
      • 취미생활 (167)
        • 잉여로운 칵테일 (2)
        • 잉여의 식물키우기 (130)
        • 잉여로운 여행기 (11)
        • 잉여의 제2외국어 (21)
        • 잉여로운 책장 (2)
      • Java (1)
        • Java의 정석 (1)
      • 꿀팁 공유 (3)
  • 태그

    리액트
    next.js
    자바스크립트
    리얼학습일기
    프로그래머스
    알고리즘
    ChatGPT
    네트워크
    typescript
    CSS
    타일러영어
    react
    javascript
    식물
    영어회화
    영어독학
    타입스크립트
    바질
    webpack
    리얼클래스
    바질 키우기
    덤프
    Node.js
    ReactNative
    CCNA
    Babel
    redux
    다이소
    Docker
    네이버 부스트캠프
  • hELLO· Designed By정상우.v4.10.1
잉여개발자
[React] Context Module Function 패턴
상단으로

티스토리툴바