상태 업데이트에 주로 썼던 useState 뿐만 아니라 useReducer로도 업데이트 가능
useState()에서는 설정하고 싶은 다음 상태를 직접 지정해주는 방식으로 업데이트
useReducer()에서는 action을 기반으로 상태 업데이트, 업데이트 시 action참조
업데이트 시 참조하고 싶은 값이 있다면 dispatch({})안에 넣어주면 됨
useReducer는 상태 업데이트를 Component에서 분리시킬 수 있음
다른 파일에서 작성 후 불러오는 것도 가능
reducer : 상태를 업데이트하는 함수
function reducer(state, action){
switch(action.type){
case '~~':
return //do Something
}
}
형태는 위와 같음
reducer는 현재 상태와 action객체를 매개변수로 받아와서 새로운 상태를 반환해 줌
이 reducer함수를 useReducer의 첫번째 매개변수로 넣어줘야 함
const [number, dispatch] = useReducer(reducer, 0);
0은 number의 초기값이자 기본값임
숫자 뿐 아니라 문자열, 객체도 가능
number : 현재 상태
dispatch : action을 발생시키는 함수 JSP에서의 forwardDispatcher처럼 보내는 의미
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;
useState로 구현한 단순한 카운터 프로그램
import React, { useReducer } from "react";
function reducer(state, action) {
switch (action.type) {
case "INCREMENT":
return state + 1;
case "DECREMENT":
return state - 1;
default:
throw new Error("Unhandled action");
}
}
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로 구현한 단순한 카운터 프로그램
정리하면 useReducer()의 두번째 매개변수인 0이 number에 처음 적용이 되고, +1버튼이 클릭되면, dispatch에 의해 action이 발생하고 reducer함수가 동작 state는 초기값이자 number값과 동일한 0이고, +1을 해서 1을 return
-1도 마찬가지로 dispatch에 의해 현재 number의 값이 state로 전달되고 계산된 값 state-1을 return
두번째 예제, onToggle과 onRemove기능이 있는 페이지 기능 구현
<UserList
users={users}
onToggle={onToggle}
onRemove={onRemove}
></UserList>
Component에 다음처럼 넘겨주는 App.js가 있다고 가정
각 user들에는 id가 있다. 따라서, onToggle이나 onRemove 클릭시, id를 전달받을 수 있음
<button onClick={() => onRemove(id)}>삭제</button>
위의 경우 App.js 에서 처리 방법은 다음과 같음
function reducer(state, action) {
switch (action.type) {
case "TOGGLE_USER":
return {
...state,
users: state.users.map(user =>
user.id === action.id ? { ...user, active: !user.active } : user
)
};
case "REMOVE_USER":
return {
...state,
users: state.users.filter(user => user.id !== action.id)
};
default:
throw new Error("Unhandled action");
}
}
dispatch 부분은 밑과 같음
const onToggle = useCallback(id => {
dispatch({
type: "TOGGLE_USER",
id
});
}, []);
const onRemove = useCallback(id => {
dispatch({
type: "REMOVE_USER",
id
});
}, []);
UserList Component로부터 받은 id값을 dispatch를 통해 reducer함수에 전달
//onToggle
users: state.users.map(user =>
user.id === action.id ? { ...user, active: !user.active } : user
)
위의 경우에서 users는 return되어 onToggle함수 안에 쓰이는 배열형 객체
state.users는 dispatch를 호출한 시점에서 넘겨지는 state
//useReducer 사용부분
const [state, dispatch] = useReducer(reducer, initialState);
쉽게 풀어서 말하면 dispatch로 호출한 시점의 state를 사용해 새로운 상태를 만들고 (위를 예로 들면 state.users가 새로운 상태로 변경됨) 그것을 return하면, 새로운 state가 호출한 시점의 state를 덮어씌우게 됨
즉, function App()의 state와 function reducer(state, action)의 state는 같으면서 같지 않음. dispatch로 호출시 App state가 reducer state가 되지만, 후에 reducer state가 return한 값이 App state가 됨