reducer
: 현재 상태와 액션 객체를 파라미터로 받아와서 새로운 상태를 반환해주는 함수이다.
function reducer(state, action) {
// 새로운 상태를 만드는 로직
// const nextState = ...
return nextState;
}
reducer 에서 반환하는 상태는 곧 컴포넌트가 지닐 새로운 상태가 되게 된다.
- action : 업데이트를 위한 정보이다. 주로 type값을 지닌 객체 형태로 사용되기도 한다.
useReducer
useReducer를 쓸려면 reducer의 구조를 알아야 되서 살펴보았다. useReducer()함수는 첫번째 인자로 넘어오는 reducer 함수를 통해 컴포넌트의 상태(state)가 행동(action)에 따라 어떻게 변해야하는지를 정의한다.
reducer 함수는 switch 분기문을 이용하면 이해하기 쉽게 작성할 수 있다.
아래는 useReducer의 구조이다.
const [state, dispatch] = useReducer(reducer, initialState);
- state : 컴포넌트에서 사용 할 수 있는 상태이다.
- action : 액션을 발생시키는 함수이다.
useReducer에 넣는 첫번째 파라미터는 reducer 함수이고, 두번째 파라미터는 초기 상태이다.
비교
선언
1) useState
import { useState } from "react";
function EditCalendarEvent() {
const [event, setEvent] = useState({
title: "",
description: "",
attendees: [],
});
return (
<>
<input
value={event.title}
onChange={(e) => setEvent({ ...event, title: e.target.value })}
/>
{/* ... */}
</>
);
}
2) useReducer
import { useReducer } from "react";
function EditCalendarEvent() {
const [event, updateEvent] = useReducer(
(prev, next) => {
return { ...prev, ...next };
},
{ title: "", description: "", attendees: [] }
);
return (
<>
<input
value={event.title}
onChange={(e) => updateEvent({ title: e.target.value })}
/>
{/* ... */}
</>
);
}
→ useReducer 훅을 사용하면 상태 A에서 상태 B로의 변환을 제어할 수 있다.
위와 같이 useState를 사용해서 useReducer같이 사용할 수 있다. 그러나, 이러한 포맷은 항상 ...event 로 전개하여 객체를 직접 변경하지 않도록 해야 한다.
반대로, useReducer는 이는 상태를 한 곳에서 관리하며 언제나 유효하다는 것을 보장한다.
1) useRededucer를 쓰기 전
import React, { useState } from 'react';
function Counter() {
const [number, setNumber] = useState(0);
const onIncrease = () => {
setNumber(prevNumber => prevNumber + 1);
};
const onDecrease = () => {
setNumber(prevNumber => prevNumber - 1);
};
return (
<div>
<h1>{number}</h1>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
);
}
export default Counter;
2) useRededucer를 쓴 후
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
function Counter() {
const [number, dispatch] = useReducer(reducer, 0);
const onIncrease = () => {
dispatch({ type: 'INCREMENT' });
};
const onDecrease = () => {
dispatch({ type: 'DECREMENT' });
};
return (
<div>
<h1>{number}</h1>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
);
}
export default Counter;
→ 확실히 다른 예시들을 참고해서 보았을 때 useReducer의 경우 가독성있게 정리가 가능하다. useState는 여러개의 변수를 하나씩만 선언해줘야 하기에 useReducer를 바로 갖다쓸 수 있는 것이 장점이다.
useReducer vs useState
useRededucer와 비슷하게 쓰이는 것이 useState이다. 어떨 때 useReducer를 쓰고 어떨 때 useState 를 써야 할까라는 질문이 구글링하면서 많이 나왔었다. 그런데 이에 대한 답은 정답이 없는 것 같다.
예를 들어, 컴포넌트에서 관리하는 값이 딱 하나고, 그 값이 단순한 숫자, 문자열 또는 boolean 값이라면 확실히 useState로 관리하는 것을 권장한다.
const [value, setValue] = useState(true);
하지만, 만약에 컴포넌트에서 관리하는 값이 여러개가 되어서 상태의 구조가 복잡해진다면 useReducer 로 관리하는 것도 낫다.
setUsers(users => users.concat(user));
setInputs({
username: '',
email: ''
});
느낀점
위에서도 말했다 싶이 나는 useState로만 개발을 해왔다. 그 이유는 상태관리를 처음에 입문할 때 접해서 너무 장벽이 높다고 생각해서 쓸 염두를 안 두었다. 그런데 이렇게 useReducer를 쓰면 변수를 하나하나 쓰던것을 한번을 쓸 수 있고, 이를 한번에 관리할 수 있으니 제일 큰 장점이 이것이라고 생각한다. 다음에 내가 쓴 코드를 고칠 일이 있다면 이렇게 쓰는 것도 좋다고 생각한다.
import { useState } from "react";
function EditCalendarEvent() {
const [startDate, setStartDate] = useState();
const [endDate, setEndDate] = useState();
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [location, setLocation] = useState();
const [attendees, setAttendees] = useState([]);
return (
<>
<input value={title} onChange={(e) => setTitle(e.target.value)} />
{/* ... */}
</>
);
}
이러한 경우를 의미한다.
🔗 참고
https://react.vlpt.us/basic/20-useReducer.html