[Study] React _ TypeScript 적용하기 #2

kyle kwon·2022년 11월 16일
0

React

목록 보기
9/15
post-thumbnail

Prologue

이번주 스터디는 지난 React에 점진적으로 TypeScript 적용하기 #1에 이어서 #2 입니다.
이번에는 contextAPI + useReducer를 활용할 때 type을 적용하는 방법 위주로 정리합니다.




Context Type 지정 및 사용

context API를 활용할 때 주로 사용하는 함수와 요소는 다음과 같습니다.

  1. createContext()
  2. useContext()
  3. <Context.Provider value={value}></Context.Provider>

이전에 context API는 전역으로 상태를 관리하기도 할 때 또는 재사용성이 높은 컴포넌트를 만들 때 사용하곤 하는데, 이 때 useReducer hook과 같이 종종 사용한다고 말했었습니다.

그러면, 본격적으로 type 지정을 어떻게 하는 지 살펴보면 다음과 같습니다.


createContext()

interface TodoType {
  id: number
  text: string
  isChecked: boolean
}

interface TodoStateType {
  todos: TodoType[]
}

const TodoStateContext = createContext<TodoStateType | null>(null)

TodoList App에서 활용한 context 코드의 일부입니다. createContext를 통해 Context를 생성할 텐데요.
이 때, Provider 컴포넌트를 통해 전달한 value의 type을 genericunion type을 이용하여 TodoStateType | null로 타입을 지정합니다.


<Context.Provider value={Value}></Context.Provider>

// TodoProvider.tsx


function TodoProvider({children}: {children : React.ReactNode}) {
  const [todoState, todosDispatch] = useReducer(todoReducer, { todos: loadTodos() })
  
  return (
    <TodoStateContext.Provider value={todoState}>
            {children}
    </TodoStateContext.Provider>
  )
}

위의 createContext에서 인자로 받는 값 또는 참조값의 타입을 지정해주었기 때문에, Provider에서 value를 이용해 전역으로 상태를 공유하는 값 todoState은 타입 오류 없이 잘 작동하게 됩니다.


useContext()

useContext는 전역으로 관리하는 상태 (여기서는 useReducer가 반환하는todoState)를 사용할 컴포넌트에서 import하여 사용해도 상관없지만, hook 함수로 만들면 상태를 사용하길 원하는 컴포넌트에서 활용할 때 유용하게 사용 가능합니다.

다음과 같이 사용합니다.

export const useTodoState = () => {
  const value = useContext(TodoStateContext)

  if (!value) {
    throw new Error('cannot find useTodoState')
  }

  return value
}

if 조건문이 존재하는 이유는 위에서 보았듯, createContext를 통해 context 생성 시 초기값을 null로 주었습니다. 이후에 Providervalue를 전달하여 useContext를 사용할 때, 잘못된 값을 전달할 때 에러를 잡아내기 위해서, value가 null이면 error를 확인할 수 있도록, 조건문을 추가하였습니다.




useReducer hook Type 지정 및 사용

관리해야 할 상태가 참조값, 즉 배열 또는 객체인 경우 내부 필드(key 또는 Property)가 많아지면, useState를 여러 개로 상태를 관리하는 경우들이 발생합니다.

이 때 useReducer를 활용하면, useReducer가 인자로 받는 순수 함수reducer 함수에서 조건문 switch-case를 활용해서 dispatch가 받는 action 객체가 다양하여도, 가독성이 늘어난다는 장점이 있습니다.

function reducer(state, action) {}

//todoReducer.ts

interface TodoInputStateType {
  text: string
}

type TodoInputActionType =
  | {
      type: 'change'
      payload: string
    }
  | { type: 'clear' }


export function todoInputReducer(state: TodoInputStateType, action: TodoInputActionType) {
  switch (action.type) {
    case 'change':
      return {
        text: action.payload,
      }
    case 'clear':
      return {
        text: '',
      }
    default: 
      throw new Error('this is not action type')
  }
}

useReducer에 사용되는 reducer를 선언할 때 type은 인자로 받는 stateaction만 지정하면 됩니다.

statetext를 필드로 갖는 객체이며, action 객체는 typepayload라는 필드가 지정된 객체입니다. 위에서는 switch-case 조건문을 활용하기 때문에, 우리가 지정한 type에서만 action.type, 즉 case가 사용되도록 사전에 에러를 방지할 수 있습니다.



reducer 함수가 사용되는 useReduceruseReducer가 반환하는 Disptach 함수inputDispatch를 전역 상태로 전달하는 creatContext 함수를 살펴보면 다음과 같습니다.

createContext() & useReducer(reducer, initialState)

// TodoProvider.tsx

type TodoInputActionType =
  | {
      type: 'change'
      payload: string
    }
  | { type: 'clear' }


const InputTodoDispatchContext = createContext<React.Dispatch<TodoInputActionType> | null>(null)


function TodoProvider(){
 	const [inputState, inputDispatch] = useReducer(todoInputReducer, { text: '' })
    
    return (
    	<InputTodoDispatchContext.Provider value={inputDispatch}>
            {props.children}
          </InputTodoDispatchContext.Provider>
    )
}

이번에는 value로 전달하는 전역 상태를 inputDispatch로 활용해 봤습니다.
코드의 상단을 보면, Dispatch 함수의 타입을 createContext()로 컨텍스트 생성 시 <React.Dispatch<TodoInputActionType> | null>로 지정한 것을 볼 수 있습니다.

해당 type은 Dispatch라는 함수를 받는 것이고, Dispatch 함수는 인자로 받는 Action 객체의 타입을 generic으로 받기 때문에 위와 같이 type을 지정합니다.




Conclusion

이렇게 React에 Typescript를 적용할 때, context API와 useReducer 사용시 타입을 지정하는 방법에 대해서 정리해 보았습니다. 다음에는 또 다른 전역상태 라이브러리인 redux와 이 redux를 쉽게 사용할 수 있게 도와주는 redux-toolkit을 어떻게 사용하는 지 익히고, 정리하는 시간을 가져보겠습니다.

감사합니다.

profile
FrontEnd Developer - 현재 블로그를 kyledot.netlify.app으로 이전하였습니다.

0개의 댓글