reducer
함수는 현재 state(상태)와 action(행동)객체를 인자로 받아 새로운 state를 반환하는 함수.dispatch
함수는 컴포넌트 내에서 상태 변경을 일으키기 위해서 사용되는데 인자로 action객체를 받음.action
객체는 어떤 부류의 행동인지를 나타내는 type 속성과 해당 행동과 괸련된 data를 담고 있음.dispatch함수에 action을 던지면, reducer함수가 이 행동(action)에 따라서 state를 변경해줌.
import { useReducer } from "react";
const reducer = (state, action) => {
switch (action.type) {
case "INCREASE":
return state + 1;
case "DECREASE":
return state - 1;
default:
break;
}
};
export default function Counter() {
const [state, dispatch] = useReducer(reducer, 0);
return (
<div>
<button
onClick={() => {
dispatch({ type: "INCREASE" });
}}>
+
</button>
{state}
<button
onClick={() => {
dispatch({ type: "DECREASE" });
}}>
-
</button>
</div>
);
}
코드 설명
- useReducer는 reducer함수와 초기 state를 인자로 받음. useReducer함수는 state와 dispatch함수를 반환.
- +버튼을 누르면 dispatch함수에 action을 던지는 데 이 때 reducer함수가 작동하여 state를 변경.
-> 사실 이렇게 간단한 상태관리는 useState 훅을 쓰는 것이 더 좋을 수도 있음.
-> useReducer함수는 복잡한 상태관리를 할 때 빛을 발함.
코드 설명 (출처: velopert님)
간단하게 기능에 대해 설명해보자면
- 계정명과 이메일을 작성하고 등록버튼을 누르면 사용자를 등록할 수 있음.
- 계정명을 클릭하면 활성 사용자로 등록하거나 해제할 수 있음.
- 사용자 옆 삭제버튼을 누르면 사용자를 삭제할 수 있음.
원래의 나라면 useState를 통해 두가지 상태로 분리하여 관리를 했을 것이다.
// 리액트 컴포넌트 안에서 정의
const [inputs, setInputs] = useState({username:"", email:""})
const [users, setUsers] = useState([])
const onCreate = () => {
setUsers([...users, {...inputs, active: false}]);
setInputs({ username: "", email: ""});
};
const initialState = {
inputs: {
username: '',
email: ''
},
users: [
{
id: 1,
username: 'velopert',
email: 'public.velopert@gmail.com',
active: true
}
};
function reducer(state, action) {
switch (action.type) {
case 'CREATE_USER':
return {
inputs: initialState.inputs,
users: state.users.concat(action.user)
};
default:
return state;
}
}
///////////////////////////////////////////////////
// 리액트 컴포넌트안에서 정의
const onCreate = () => {
dispatch({
type: 'CREATE_USER',
user: {
id: nextId.current,
username,
email
}
});
nextId.current += 1;
}
-> state를 위에서 두 가지로 분리하지 않고, inputs과 users정보 모두 하나의 state로 관리하는 것을 볼 수 있다.
-> useReducer함수를 사용하면 컴포넌트의 상태 업데이트 로직(reducer함수)를 컴포넌트에서 분리시킬 수 있다. 상태 업데이트 로직을 컴포넌트 바깥에 작성 할 수도 있고, 심지어 다른 파일에 작성 후 불러와서 사용 할 수도 있다.
(onCreate함수도 분리할 수 있지 않을까 생각했다. onCreate함수는 dispatch함수를 사용하고 있다. dispatch함수는 useReducer훅을 통해 반환되는데 리액트 훅의 경우 리액트 컴포넌트 안에서만 사용할 수 있기 때문에 분리가 불가능할 것 같다 -> custom hook을 쓴다면 가능할지도 모르겠다)
const onCreate = () => {
setUsers([...users, {...inputs, active: false}]);
setInputs({ username: "", email: ""});
};
정리
- dispatch함수에 action을 던지면, reducer함수가 이 행동(action)에 따라서 state를 변경해줌.
- 복잡한 상태관리를 해야할 때 useReducer 훅을 쓴다.