Javascript/React

TypeScript와 함께 useReducer를 사용하는 방법

kimc 2022. 7. 16. 00:37

 


 

 

이번 글을 통해 배워갈 내용

  1. TypeScript와 함께 useReducer를 사용하는 방법

 


 

UseReducer 6가지 꿀 정보

 

1.
useReducer Hook은 useState Hook과 비슷한 성질을 가집니다.

2.
필요시 initFunction 도 받아서 쓸 수 있습니다.
const [state, dispatch] = useReducer(reducer, initialState)
const [state, dispatch] = useReducer(reducer, initialState initFunction)

3.
useReducer Hook은 reducer, initialState을 인자로 받습니다.
reducer 함수는 state의 로직을 포함하고
initialState 말 그대로 초기 상태입니다.

4.
useReducer Hook은 현재 state와 dispatch method를 반환합니다.

5.
여러 개의 값들을 state 관리할 때
useState 대신 useReducer를 많이 사용합니다.

6.
dispatch를 callbacks대신에 내려서
Performance를 높일 수 있습니다.


코드 예시

 

useReducer를 활용해서

학생을 추가 삭제 그리고 전체 삭제하는 컴포넌트입니다.

 

먼저 타입들을 선언해줍니다.

원칙상 실무처럼 파일을 나누지 않고

교육적 용도로 하나의 파일에 작성했습니다.

 

번호 타입이며 있을 수 있고 없을 수 도있는 idx 값과

문자열 타입의 name이 있습니다.

 

State에는 학생 타입의 배열이 들어있고

최초 State인 initialSchoolState에는 빈 학생 배열이 들어 있습니다.

 

액션은 학생을 추가하는 ADD_STUDENT,

하나 삭제하는 DEL_STUDENT,

그리고 전체 삭제하는 DEL_ALL 이 있습니다.

interface Props {}

type Student = {
  idx?: number;
  name?: string;
};

type State = {
  students: Student[];
};

const initialSchoolState: State = {
  students: [],
};

enum ActionKind {
  ADD_STUDENT = 'ADD_STUDENT',
  DEL_STUDENT = 'DEL_STUDENT',
  DEL_ALL = 'DEL_ALL',
}

 

reducer는 state(상태) 값으로 위에 선언한 State 타입을 받고

action으로는 type과 payload를 받습니다.

액션의 종류에 따라서 학생을 추가, 하나 삭제 혹은 전체 삭제합니다

const reducer = (state: State, action: { type: any; payload: Student }): State => {
  console.log(state);
  switch (action.type) {
    case ActionKind.ADD_STUDENT:
      return { ...state, students: [...state.students, action.payload] };
    case ActionKind.DEL_STUDENT: {
      return { ...state, students: [...state.students.filter((s) => s.idx !== action.payload.idx)] };
    }
    case ActionKind.DEL_ALL:
      return { ...state, students: [] };
    default:
      throw new Error();
  }
};

 

useReducer로 Hook을 사용하며

Render 하는 부분은 아래와 같습니다.

const LearnUseReducer: FunctionComponent<Props> = () => {
  const [state, dispatch] = useReducer(reducer, initialSchoolState);
  const { students } = state;
  return (
    <>
      {students.map((st) => {
        return <div key={st.idx}> {st.name} </div>;
      })}
      <br />
      <button
        onClick={() => dispatch({ type: ActionKind.ADD_STUDENT, payload: { idx: state.students.length + 1, name: '학생-' + state.students.length } })}
      >
        {' '}
        학생추가{' '}
      </button>
      <button onClick={() => dispatch({ type: ActionKind.DEL_STUDENT, payload: { idx: state.students.length } })}> 학생삭제</button>
      <button onClick={() => dispatch({ type: ActionKind.DEL_ALL, payload: {} })}> 학생 전체 삭제</button>
    </>
  );
};

 

 

전체 코드

import { FunctionComponent, useReducer } from 'react';

interface Props {}

type Student = {
  idx?: number;
  name?: string;
};

type State = {
  students: Student[];
};

const initialSchoolState: State = {
  students: [],
};

enum ActionKind {
  ADD_STUDENT = 'ADD_STUDENT',
  DEL_STUDENT = 'DEL_STUDENT',
  DEL_ALL = 'DEL_ALL',
}

const reducer = (state: State, action: { type: any; payload: Student }): State => {
  console.log(state);
  switch (action.type) {
    case ActionKind.ADD_STUDENT:
      return { ...state, students: [...state.students, action.payload] };
    case ActionKind.DEL_STUDENT: {
      return { ...state, students: [...state.students.filter((s) => s.idx !== action.payload.idx)] };
    }

    case ActionKind.DEL_ALL:
      return { ...state, students: [] };
    default:
      throw new Error();
  }
};

const LearnUseReducer: FunctionComponent<Props> = () => {
  const [state, dispatch] = useReducer(reducer, initialSchoolState);
  const { students } = state;
  return (
    <>
      {students.map((st) => {return <div key={st.idx}> {st.name} </div>;})}
      <br />
      <button onClick={() => dispatch({ type: ActionKind.ADD_STUDENT, payload: { idx: state.students.length + 1, name: '학생-' + state.students.length } })}> 학생추가 </button>
      <button onClick={() => dispatch({ type: ActionKind.DEL_STUDENT, payload: { idx: state.students.length } })}> 학생삭제</button>
      <button onClick={() => dispatch({ type: ActionKind.DEL_ALL, payload: {} })}> 학생 전체 삭제</button>
    </>
  );
};

export default LearnUseReducer;

 

한 줄 요약

State가 많다면 useState 대신에 useReducer를 쓰면 깔끔

 

 

참조 및 인용

https://reactjs.org/docs/hooks-reference.html

 

Hooks API Reference – React

A JavaScript library for building user interfaces

reactjs.org


https://stackoverflow.com/questions/65420468/converting-react-usereducer-to-typescript

 

Converting React useReducer to TypeScript

I am trying to convert a useReducer hooks to TypeScript, It's working fine as useReducer hooks, however not in TypeScript. Here is my code, import * as React from "react"; const JOKE_URL...

stackoverflow.com


 

728x90