useReducer
- useState처럼 State를 생성하고 관리할 수 있게 해주는 도구
- 여러개의 하위 값을 포함하는 복잡한 State를 다뤄야 할 때 useState 대신 useReducer를 사용하면 훨씬 간결하게 코드 작성 가능
- 유지보수도 편해짐
- Reducer, Dispatch, Action 세가지로 이루어짐
Reducer, Dispatch, Action

- 여름이가 은행에서 만원을 출금했다고 생각하기
- 여름이가 은행을 방문해서 "만원을 출금해주세요"라고 요구하면 여름이의 계좌 거래내역에서 만원이 빠져나갔다고 기록이 됨
- 여름이는 직접적으로 자신의 거래내역을 업데이트 시키지 않음 -> 여름이는 은행에게 요구를 하고 은행은 여름의 요구에 따라 거래내역을 업데이트 해줌
- 거래내역을 State라고 생각하기
- 은행을 Reducer라고 생각하기
- 여름이가 은행에게 거래내역을 업데이트 해달라고 하는 행위를 Dispatch라고 생각하기
- "만원을 출금해주세요"라는 요구 내용을 Action이라고 생각하기
Reducer (은행 역할)
- State를 업데이트 해주는 역할
- Component의 State를 변경하고 싶다면 꼭 Reducer를 통해야 함
- 여름이가 거래내역을 업데이트 하고 싶으면 은행이 대신 해줘야 하는 것과 같음
Dispatch (여림이의 요구 행위)
- 여름이가 은행에게 거래내역을 업데이트 해달라고 하는 행위와 같음
Action (요구 내용)
- "만원을 출금해주세요"라는 요구 내용이 Action임
정리
-
여름이는 은행에게 요구라는 Dispatch 안에 내용이라는 Action을 담아서 은행이라는 Reducer에 보냄으로써 State를 변경할 수 있음
-
여름이가 거래내역이라는 State를 업데이트 하기 위해선 요구라는 Dispatch에 "만원을 출금해주세요"라는 내용인 Action을 넣어서 은행이라는 Reducer에 전달을 해줘야 함
-
그럼 은행은 Action의 내용대로 State를 업데이트 시켜줌
-
여름이는 은행이라는 Reducer에게 다른 Action들을 보냄으로써 예금, 출금, 송금 등 복잡한 일을 할 수 있음
컴포넌트의 관점으로 보기

- State를 업데이트 시켜주기 위해서는 Dispatch 함수의 인자로 Action이라는 것을 넣어서 Reducer에게 전달을 해줌
- 그럼 Reducer가 컴포넌트의 State를 Action안의 내용대로 업데이트 시켜줌
실제 코드 구현



- useReducer는 useState와 비슷하게 배열을 반환해줌
- 배열의 첫 번째 요소 : 새로 만들어진 State
- 배열의 두 번째 요소 : useReducer가 만들어준 dispatch 함수

- useReducer는 두 개의 인자를 받음
- 첫 번째 인자 : reducer (새로 생성해줘야 함)
- 두 번째 인자 : 배열의 첫 번째 요소의 새로 만들어진 State에 들어갈 초기값

- reducer는 두 개의 인자를 받음
- 첫 번째 인자 : 현재 State
reducer 함수가 불리는 시점에 useReducer 배열의 첫 번째 요소인 새로 만들어진 State 안에 들어간 값이 들어가게 됨
- 두 번째 인자 : action
reducer에게 State를 변경해달라고 요구할때 그 요구에 대한 내용이 들어감

- money State는 reducr를 통해서만 수정 가능
- money를 수정하고 싶을 때마다 dispatch를 불러줌
- dispatch는 useReducer가 만들어준 함수, 사용할 때 인자로 action을 넣어줌
- dispatch를 부르면 reducer가 호출이 됨 -> 인자로 action이 전달이 됨 -> action을 토대로 reducer는 Stete를 변경해줌

- action은 보통 objcet 형태로 보내고, 그 안에 type을 넣음
- type안에는 reducer에게 요구하고 싶은 내용을 넣으면 됨
- payload 안에는 현재 input에 들어있는 값을 넣어주기


- reducer가 action에 나와있는대로 State를 업데이트 시켜주도록 코드 작성
- reducer가 return하는 값이 새로 업데이트 될 State가 됨



-
useReducer도 useState와 같이 State가 바뀔 때마다 컴포넌트를 렌더링 해줌
-
우리는 예금, 출금 모두 구현해야 하기 때문에 항상 state + action.payload를 사용하면 안됨
-
action안에 들어있는 type에 따라 다르게 state를 업데이트 해줘야 함
-
그래서 보통 reducer안에 if else 문 또는 switch문을 많이 사용함






만약 코드를 더 깔끔하게 만들고 싶다면?
- action의 type을 따로 constant로 빼주면 됨
- constant 내의 문자열만 수정해주면 나머지도 다 업데이트가 되기 때문에 유지/보수에 편리함
기존 코드


변경 코드


실행 화면
1) 예금 버튼 클릭시


2) 출금 버튼 클릭시


실제 코드 구현 2
UseReducerHook2
import React, { useReducer, useState } from "react";
import Student from "../components/Student";
const reducer = (state, action) => {};
const initalState = {
count: 0,
students: [
{
id: Date.now(),
name: "been",
isHere: false,
},
],
};
function UseReducerHook2() {
const [name, setName] = useState("");
const [studentInfo, dispatch] = useReducer(reducer, initalState);
return (
<div>
<h1>출석부</h1>
<p>총 학생 수 : {studentInfo.count}</p>
<input
type="text"
placeholder="이름을 입력해주세요"
value={name}
onChange={(e) => setName(e.target.value)}
></input>
<button>추가</button>
{}
{studentInfo.students.map((student) => {
return <Student key={student.id} name={student.name} />;
})}
</div>
);
}
export default UseReducerHook2;
Student
import React from "react";
const Student = ({ name }) => {
return (
<div>
<span>{name}</span>
<button>삭제</button>
</div>
);
};
export default Student;

학생 추가하는 기능 만들기
- studentsInfo 안에 students를 넣어주면 됨
- state를 변경하는거니까 reducer 안에서 코드 수정

- 추가버튼에 add-student type의 action을 담은 dispatch 설정해주기

- reducer에서 switch문 사용해서 조건 설정해주기


- initialState에서 been은 필요 없으니까 student를 빈 배열로 바꿔주기


- switch문에 default, delete-student case 추가해주기

- student 컴포넌트의 prop으로 dispatch 보내주기

- Student 컴포넌트에 가서 dispatch와 id 받기
- 삭제 버튼에 dispatch를 불러서 type과 payload 설정해주기

- UseReducerHook2 컴포넌트에서 delete-student return 부분 코드 작성해주기


학생 출석 여부 기능 구현


- UseReducerHook2 파일에서 isHere을 true로 변경해주기
- Student 컴포넌트에 student.isHere 보내주기

- Student 파일로 가서 prop에 isHere 받아주고 span태그 style 속성 변경해주기


- onClick 속성을 통해 학생의 이름을 클릭할때마다 true -> false, false -> true로 바뀌게 해주기

- reducer에 가서 mark-student case 추가해주기



- 이름 클릭시 style이 잘 적용이 됨
- 이름을 다시 클릭하면 style이 해제됨