[꼼꼼한 개발자] 꼼코더
[React] - useReducer()란 간단하고 쉽게 이해하기(예제코드, useReducer 사용예제) 본문
🧹 쉬운 정리
1. useReducer()는 useState()와 같은 상태 관리, 상태 업데이트 훅(Hook)이다.
2. 변경할 값이 많을 때(상태 업데이트) 즉 상태 관리할 데이터가 많아질 때 사용을 고민해 볼 필요가 있다.
3. 좀 더 구조화된 방식으로 상태를 관리하고 싶을 때 사용할 수 있다. ( 예) "PLUS" 타입 은 무조건 더하기)
4. 선언형태 : const [state, dispatch] = useReducer(reducer, initialState);
- state : 상태 이름 (컴포넌트에서 사용할 상태) > 빵(재료) 담는 접시
- dispatch : 상태(state)를 변경 시 필요한 정보를 전달하는 '함수' > 주문서
- reducer : dispatch를 확인해서 state를 변경해 주는 '함수' > 주방(공장)
- initialState : state에 전달할 초기 값 > 빵(및 재료 등) 개수 설정
👀 서론
리액트에서 상태 관리(업데이트)를 다루는 훅은 2가지 종류가 있다.
- useState()
- useReducer()
useReducer를 처음 봤을 때 어렵게 느껴질 것이다. 하지만 추후에 Redux 이해에도 많은 도움이 되는 개념일 뿐더러
useState만 사용할 수 있는 '선택불가한 개발자'보다는 useState와 useReducer중 '선택가능한 개발자가 되길 바라며
그래도 이 글을 읽을 때 중간에 포기하여 나가지 말고
꼭 끝까지 봐줬으면 한다! 개념 익힘, 학습에 정말 도움이 될 거라고 확신한다.
이 글에서는 useReducer()에 대해서 비유와 함께 쉽게 이해할 수 있도록 작성해 보았다! 😀
(추천하진 않지만 정 급하신 분들은 최하단 예제 코드 참고)
(참고) useReducer()에 3가지 인자 값 중에서 'init'이라는 3번째 인자 값(함수) 또한 있지만
이번 글에서는 useReducer()의 개념을 간단히 알아가고자 'init(함수)'는 사용하지 않는다.
💡 useReducer()란?(useState와 차이점)
리액트(React)의 useReducer는 상태 관리와 상태 업데이트를 다루는 React 훅 중 하나.
이 훅은 주로 복잡한 상태 관리 로직을 다루거나 여러 컴포넌트 간에 상태를 공유할 때 사용.
useReducer는 클래스 컴포넌트에서의 setState 메서드와 유사한 역할을 하지만
좀 더 구조화된 방식으로 상태를 관리할 수 있도록 도와준다.
🧑🏻🍳 사용 방법
먼저 개념부터 안내 후 확인했던 개념들을 코드예제를 통하여 정말 정말 쉽게 알려주겠다!
🏟️ useReducer() 선언과 initialState
useReducer()를 사용하기 위한 초기 선언방식부터 살펴본다!
const [state, dispatch] = useReducer(reducer, initialState);
state : 상태 이름 (컴포넌트에서 사용할 상태) > 빵 재료 담는 접시
dispatch : 상태(state)를 변경 시 필요한 정보를 전달하는 '함수' > 주문서
reducer : dispatch를 확인해서 state를 변경해 주는 '함수' > 제작
initialState : state에 전달할 초기 값 > 빵 재료
initialState 초기 값은 객체, 배열 등 다양한 값으로도 전달할 수 있다.
⭐️ 비유설명
const [state, dispatch] = useReducer(reducer, 3);
🙋🏻♂️ 빵 제작 요청자 : "음 빵 재료를 '처음에는' 3개로 해야지~ (initialState)" -> "다 골랐으니 접시에 넣어야 겠다!(state)" ->
👨🏻🍳 주방장 : "다들 주목! 우리 주방은 정해진 '주문서(dispatch)'대로만 '제작(reducer)'한다 알겠나?"
🧑🏻🍳 주방원들 : "넵!"
여기서 잠깐 dispatch, reducer는 내가 만든 적도 없는데 이거는 죄다 뭐지??
아래에서 먼저 reducer부터 확인해 보자 (dispatch는 3번)
👨🏻🍳 reducer()와 action에 대해서
reducer()는 2가지 인자를 받는다
- state : 위에서 선언한 state값 이 들어간다. (초기 값은 당연히 initialState에서 설정한 값)
- action : 업데이트를 위한 정보를 가지고 있는 '객체' 즉 위에서 선언한 dispatch라고 생각 (주문서)
(그렇다면 action 객체는 어떻게 생겼을까? 추후 아래에서 설명 우선 reducer() 비유 설명부터 보자!)
⭐️ 비유설명
function reducer(state, action) {
switch (action.type) {
case 'PLUS':
return state + 1;
case 'MINUS':
return state - 1;
default: return state;
}
}
👨🏻🍳 주방장 : "우리 주방은 '주문서(dispatch)' 속 주문내역이 'PLUS' 라면 어떠한 재료(state)가 오더라도 무조건 + 1해 알겠어!?
🧑🏻🍳 주방원들 : "넵!"
👨🏻🍳 주방장 : "이것이 내가 적어놓은 제조 방식(reducer)이니 적어놓은 대로만 실행하란 말이야! 알겠어!?"
(action 객체를 확인해 보자.. 이 부분만 확인하면 끝이다! 좀 만 힘을 내보자)
🥘 action객체(dispatch)
action객체 즉 dispatch는 위에 비유로 설명하자면
'빵 주문자가 주방에 전달할 '주문서'이다.
이곳에는 reducer()에 적어놨던 type을(주문 명령어) 적는다.
또한 reducer에서 필요할 데이터, 데이터 값(빵 재료들)도 같이 전달할 수 있다.
작성 시 무조건 따라야 하는 규칙은 아니지만 흔히 말하는 '국룰'은
'type 속 액션(값)은 대문자와 '_'로 구성하자'
// 1을 더하는 액션(주문 명령)
{
type: 'PLUS'
}
// 1을 빼는 액션(주문 명령)
{
type: 'MINUS'
}
// input 값을(빵 속 재료 등) 변경하는 액션(주문 명령)
{
type: 'CHANGE_INGREDIENT',
ingredient : 'corn',
price : 3000
}
// 새로운 객체(빵)를 생성하는 액션 (주문 명령)
{
type: 'CREATE_BREAD',
bread: {
name: 'corn_bread',
price: 4500
},
ingredient: {
name: 'corn',
value: '300g'
}
}
⭐️ 비유설명
dispatch는 말했지만 state를 변경할 수 있는 "명령어"와 정보들을 세팅하는 곳이라고 생각하면 된다.
보통 아래 2가지 형식으로 많이 사용한다.
// 1번째 방법
const onPlus = () => {
dispatch({type : 'PLUS'})
}
<button onClick={onPlus}>Plus</button>
/// 2번째 방법
<button onClick={() => dispatch({ type: 'PLUS' })}>Plus</button>
🙋🏻♂️ 빵 제작 요청자 : "저기 주방장님! 여기 'PLUS' 1회 요청이요!"
이제 예제에 적용해서 비유와 함께 쉽게 확인해 보자.
🧑🏻💻 예제 코드
코드를 먼저 읽어보고
아래에 동작 흐름에 따른 비유도 읽어보면 좋을 듯하다!!
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'PLUS':
return state + 1;
case 'MINUS':
return state - 1;
default:
return state;
}
}
function Baking() {
// 3을 value 저장
// 위에 선언했던 값을 변경하는 reducer 함수를 넣어주기!
// reducer속 로직들을 실행시킬 명령어가 담겨있는 dispatch 선언
const [value, dispatch] = useReducer(reducer, 3);
const onPlus = () => {
dispatch({ type: 'PLUS' });
};
const onMinus = () => {
dispatch({ type: 'MINUS' });
};
return (
<div>
<h1>{number}</h1>
<button onClick={onPlus}>+1</button>
<button onClick={onMinus}>-1</button>
</div>
);
}
export default Baking;
⭐️ 비유 설명
흐름 : 초기 랜더링 -> useReducer() 선언 -> '+1' 버튼 클릭
👨🏻🍳 주방장 : "다들 주목! 우리 주방은 정해진 '주문서("dispatch")'대로만 '제작(reducer)'한다 알겠나?"
🧑🏻🍳 주방원들 : "넵!"
👨🏻🍳 주방장 : "PLUS 요청이 오면 재료를 어떻게 하라고?'
🧑🏻🍳 주방원들 : "현재 재료 상태(number)에서 +1 합니다!"
🙋🏻♂️ 빵 제작 요청자 : "음 빵 재료 밀가루를 '처음에는' 3개로 해야지~ (initialState)" -> "다 골랐으니 접시에 넣어야겠다!(state)" -> '+1' 버튼을 눌러서 > "PLUS" 주문 요청을 통해서 밀가루를 4개로 만들어달라고 해야겠다!
( [+1] 버튼 클릭)
👨🏻🍳 주방장 : "다들 주목! 방금 dispatch를 통해서 action을 취해달라고 요청이 왔어 여기 손님은.. 어디 보자.. type은..'PLUS'네? 그리고 밀가루를 3개를 같이 보내주었네..? 에헤이 이전 손님은 옥수수여서 편했는데.. 아무튼 우리는 PLUS 요청이 오면 재료를 어떻게 하라고?'
🧑🏻🍳 주방원들 : "넘어온 재료를 (number) +1 합니다!"
👨🏻🍳 주방장 : "뭐 해 당장 진행시켜!(reducer 속 case 실행)
🙋🏻♂️ 마무리
제 설명이 잘 전달이 됐을지 모르겠네요..
저도 최근에 공부한 초보자라.. 습득한 지식이라 잘못된 지식이 있다면 지적 매우 환영합니다!
추후 초기 값을 객체를 넣고 reducer에서 spread 방식을 사용하여 값을 저장하는 방식을 사용하다 보면 어려워질 수 있어 보입니다!
따라서 useState(), useReducer() 등을 각 상황에 맞게 잘 사용해 보는 고민도 해보시면 좋을 듯합니다!
많은 도움이 되셨길 바라며 그럼 이만!