useReducer

ANN·2026년 1월 4일

OneBiteReact

목록 보기
6/6
post-thumbnail

📌 useReducer를 소개합니다

컴포넌트 내부에 새로운 state를 생성하는 리액트 훅
➡️ 모든 useState는 useReducer로 대체 가능

차이점은,
상태 관리 코드를 컴포넌트 외부로 분리 가능하다는 것


useState는 “현재 렌더링 중인 컴포넌트 인스턴스”에 상태를 붙이는데,
컴포넌트 밖(모듈 최상단 등)에서 상태 관리 코드를 쓰면 “어느 컴포넌트의 상태인지” 알 수 없기 때문

따라서 useState를 통해서 state를 생성하면,
onCreate처럼 state를 관리하는 코드, 즉 상태를 관리하는 코드를 컴포넌트 내부에 작성해야 함

useReducer를 사용하면,
컴포넌트 내부에선 state 생성만 하고,
state 관리는 Reducer라는 함수를 통해 컴포넌트 외부에서 관리하도록 코드 분리


이전에는 todo 데이터를 관리하는 state를 App 컴포넌트 내부에서 useState를 통해 만들었음
➡️ todos와 setTodos는 App 컴포넌트에서만 접근 가능
따라서, 이 state를 관리하는 코드 또한 반드시 App 컴포넌트 내부에만 작성이 되어야 했음

문제점‼️
state에 새로운 값을 추가하는 onCreate, 특정 값을 수정하는 onUpdate, 특정 값을 제거하는 onDelete와 같은 todo state를 관리하는 코드들이 App 컴포넌트 내부에 작성되어야 했고,
➡️ 너무 길어지게 됨

여기서 더 복잡하고, 더 다양한 상태 변화를 제공하려면?
컴포넌트 안에 더 긴 코드를 작성해야 함

하지만,
App 컴포넌트의 가장 주된 역할은 UI를 렌더링하는 것임
이와 같이 state를 관리하는 코드가 너무 많아지면 주객전도
UI를 렌더링하는 코드보다 상태를 관리하는 코드가 훨씬 더 길고 복잡하기 때문
➡️ 파일을 열었을 때 코드의 가독성이 떨어지고, 유지보수도 어려움

UI를 렌더링하는 코드와 상태 관리 코드를 분리시키기 위해 useReducer라는 리액트 훅이 필요

실습

컴포넌트 생성

테스트를 위한 컴포넌트 생성

  • 해당 컴포넌트 import
  • 컴포넌트 렌더링

이제 해당 컴포넌트에 간단한 카운터 기능 생성

  • useState와 동일하게, 새로운 state를 생성해서 배열의 첫 번째 요소로 반환
  • dispatch라고 불리는, 상태 변화를 요청하기만 하는 함수 반환
    (dispatch: 발송하다
    ➡️ 상태 변화가 있어야 한다는 사실을 알리는(발송하는) 함수)

이제 컴포넌트 내부에서 dispatch 함수를 호출하면,
상태 변화가 요청되고
그러면 useReducer가 상태 변화를 실제로 처리하게 될 함수를 호출하게 됨

➡️ 그 함수는 직접 만들어야 함


reducer 함수

컴포넌트 바깥에
reducer라는 함수를 만들고,
useReducer에 이렇게 만든 reducer라는 함수를 인수로 넣음
(첫 번째 인수)

reducer
: 변환기
➡️ 상태를 실제로 변화시키는 변환기 역할

useReducer의
첫 번째 인수는
위처럼 상태를 실제로 변화시키는 변환기 역할을 하는 reducer라는 함수를 만들어 넣고,
두 번째 인수로는 state의 초기값 전달

따라서,

컴포넌트의 최종적인 useReducer 호출은 위와 같음

컴포넌트 기능 추가

이제 버튼을 클릭하면 state값이 1씩 증가하게 해야 함

컴포넌트 내부에서, 버튼이 클릭되었을 때
dispatch 함수를 호출해서 상태 변화를 발송, 즉 요청하게 됨


dispatch

컴포넌트 안에 onClickPlus라는 함수를 만들어서
버튼의 이벤트 핸들러로 설정

그리고 이 함수 내부에서 dispatch를 호출해서 상태 변화 요청
➡️ 인수로는 상태가 어떻게 변화되길 원하는지 그 정보를 전달할 것

보통 dispatch 안에는 객체 형태의 인수

  • type : 상태를 어떻게 변화시킬지
    문자열로 적음
  • data: 증가시킬 수

➡️ + 버튼을 누르면 state값을 1 증가시키길 원함

dispatch로 상태 변화 요청 시
요청 내용: 객체("값을 1만큼 증가시켜줘")

이렇게 인수로 전달되는, 요청의 내용을 담고 있는 객체
Action 객체라고 함

Action 객체를 인수로 전달하면서, dispatch 함수를 호출하면
useReducer가 요청을 처리하기 위해
실제로 상태를 변화시키는 reducer 함수 호출


reducer 함수 완성

reducer 함수의 인수

  • 현재의 state 값
  • Action 객체

따라서 매개변수로 받은 state 값과, 액션 객체를 이용해 실제 state 값 변경

🤔 상태 변화 함수도 없는데(to~), 어떻게 state를 변경할 수 있는가?
reducer 함수에서 새로운 state를 반환하기만 하면 됨
그 반환된 값을 useReducer가 불러와서 실제 state 값을 변경시킴

매개변수 action의 type이 "INCREASE"면,
현재 상태 state에 action의 data(1)를 더한 값 반환

새로운 상태 변화 [-] 버튼

이벤트 핸들러 내 dispatch 안에 객체 전달

reducer 함수에 "DECREASE" 분기 추가
현재 상태 state에 action의 data(1)를 뺀 값 반환

분기가 많아지면,

reducer 함수 안에 action의 type이 너무 많아질 것 같으면

switch문으로 작성하는 게 일반적


📌 투두리스트 업그레이드

App 컴포넌트

  • useState를 지우고,
  • useReducer 훅 호출
    • reducer의 첫 번째 인수로 전달
    • mock 데이터를 두 번째 인수로 전달

onCreate, onUpdate, onDelete와 같은 상태변화 함수를 reducer 함수로 옮긴 후,
dispatch 함수만 호출하도록 수정

onCreate

  • 추가 버튼을 클릭하면,
  • 코드 상에 이 onCreate 함수가 실행
  • dispatch 함수가 호출
  • 그 결과로 reducer 함수가 호출될 것
  • reducer 함수에서는 현재 state값과 action 객체를 받아와,
  • 이 action 객체의 값에 따라 변화된 state 값 리턴
  • todo 아이템을 새로 생성
  • 새로운 state의 값으로 새로운 배열 반환
    • 첫 번째 요소에는 새롭게 추가될 데이터인 action.data를 넣고,
    • 그 뒤에 state로 기존의 state 값 펼쳐주기
      ➡️ 이 action.data는 onCreate 함수에서 dispatch 함수를 호출하면서, 전달한 새로운 todo 아이템

onUpdate

  • type: UPDATE
  • data 대신 targeId라는 프로퍼티로, 어떤 요소를 수정할 것인지 명시
  • map을 이용해서
  • 현재 state 안에 있는 id 중에 action의 targetId와 일치하는 요소 있는지 확인
    • 있다면, ...item으로 기존의 todo 아이템의 값을 나열하고, 해당 item의 isDone 반전
    • 없다면, 그냥 현재 값 리턴

onDelete

  • type: DELETE
  • data 대신 targeId라는 프로퍼티로, 어떤 요소를 삭제할 것인지 명시
  • filter를 이용해서
  • 아이템의 id가 action의 targetId와 같지 않은 요소만 필터링 하도록 만ㄷ름
  • 디폴트 추가

0개의 댓글