본문 바로가기

Reactjs

React Typescript Redux (Reducer 세팅하기)

반응형

리덕스를 리액트 환경에서 타입스크립트를 곁들여서 세팅하는 걸 처음부터 한번 해보자

 

우선 필요한 패키지들을 설치한다. 

 

npm install --save @types/react-redux axios react-redux redux redux-thunk

기존에 react-redux, redux, redux-thunk, axios 까지 같다면 @types/react-redux 가 새로 추가되었다고 보면 된다. 

 

npm 패키지 api를 이용해서 데이터를 가져와볼 건데 

 

https://registry.npmjs.org/-/v1/search?text=react 

위의 주소로 text 파라미터 뒤에 우리가 입력한 값이 들어간 패키지가 있으면 배열로서 그 데이터를 가져올 것이고 그 패키지 데이터들을 repository라고 프로젝트 내에서 부를 것이다. 

 

그 repository를 관리할 Reducer를 만들어보면 

 

 

interface RepositoriesState {
  loading: boolean;
  error: string | null;
  data: string[];
}

interface Action {
  type:string;
  payload?: any;
}

const reducer = (state: RepositoriesState, action: Action): RepositoriesState => {
  switch (action.type) {
    case 'search_repositories':
      return {
        loading:true, error: null, data: []
      }      

    case 'search_repositories_success':  
      return {
        loading:false, error: null, data:action.payload
      }

    case 'search_repositories-error':
      return {
        loading: false, error: action.payload, data:[]
      }

    default:
      return state;
  }
};

export default reducer;

 

위의 코드에서 볼 포인트 들은 state interface, action interface, return type이 state interface 인 것들을 볼 수 있는데 

Action 객체의 interface를 지정해주는 부분에서 조금 생소했다. 

 

하지만 위의 리듀서를 보면 3개의 Action type 값을 가지고 있는데 더 확실하게 각 액션타입에 따른 타입을 지정하려면  이 각각에 대해서도 interface를 통해 타입을 지정해 줄 수 있다. 

 

 

interface RepositoriesState {
  loading: boolean;
  error: string | null;
  data: string[];
}

interface SearchRepositoriesAction{
  type: 'search_repositories'
}

interface SearchRepositoriesSuccessAction{
  type:'search_repositories_success',
  payload:string[];
}

interface SearchRepositoriesErrorAction{
  type: 'search_repositories_error',
  payload:string;
}

const reducer = (
  state: RepositoriesState,
  action: 
    | SearchRepositoriesAction
    | SearchRepositoriesSuccessAction
    | SearchRepositoriesErrorAction
  ): RepositoriesState => {
  switch (action.type) {
    case 'search_repositories':
      return {
        loading:true, error: null, data: []
      }      

    case 'search_repositories_success':  
      return {
        loading:false, error: null, data:action.payload
      }

    case 'search_repositories_error':
      return {
        loading: false, error: action.payload, data:[]
      }

    default:
      return state;
  }
};

export default reducer;

 

 

이런식으로 각각 액션에 대해서도 따로 interface를 만들어서 문자열, 배열 등 확실하게 값에 타입을 매겨서 지정 할 수도 있다. 

 

하지만 액션이 많으면 많을 수록 reducer 함수 부분에 action annotation이 난잡해지고 길어질 텐데 이를 type으로 한데 묶어 지정해줄 수 있고 action type 문자열들도 상수로 enum에 담아 나열 해 줄 수 있다. 

 

최종적으로

 

 

interface RepositoriesState {
  loading: boolean;
  error: string | null;
  data: string[];
}


type Action =  | SearchRepositoriesAction
| SearchRepositoriesSuccessAction
| SearchRepositoriesErrorAction;

enum ActionTypes {
  SEARCH_REPOSITORIES = 'search_repositories',
  SEARCH_REPOSITORIES_SUCCESS = 'search_repositories_success',
  SEARCH_REPOSITORIES_ERROR = 'search_repositories_error'
}

interface SearchRepositoriesAction{
  type: ActionTypes.SEARCH_REPOSITORIES
}

interface SearchRepositoriesSuccessAction{
  type:ActionTypes.SEARCH_REPOSITORIES_SUCCESS
  payload:string[];
}

interface SearchRepositoriesErrorAction{
  type: ActionTypes.SEARCH_REPOSITORIES_ERROR
  payload:string;
}

const reducer = (
  state: RepositoriesState,
  action: Action
  ): RepositoriesState => {
  switch (action.type) {
    case ActionTypes.SEARCH_REPOSITORIES:
      return {
        loading:true, error: null, data: []
      }      

    case ActionTypes.SEARCH_REPOSITORIES_SUCCESS:  
      return {
        loading:false, error: null, data:action.payload
      }

    case ActionTypes.SEARCH_REPOSITORIES_ERROR:
      return {
        loading: false, error: action.payload, data:[]
      }

    default:
      return state;
  }
};

export default reducer;

 

마무리를 할 수 있으며 interface, action type enum 등 따로 폴더를 만들어서 export 해놓으면 다른 파일 들에서도 재사용이 가능하겠다. 

 

반응형