useReducer
는 useState보다 더 다양한 컴포넌트 상황에 따라 다양한 상태를 다른 값으로 업데이트해 주고 싶을 때 사용하는 Hook입니다.
리듀서는 현재 상태, 그리고 업데이트를 위해 필요한 정보를 담은 액션(action)값을 전달받아 새로운 상태를 반환하는 함수입니다.
리듀서 함수에서 새로운 상태를 만들 때는 반드시 불변성을 지켜주어야 합니다.
function reducer(state,action){
return {...}; // 불변성을 지키면서 업데이트한 새로운 상태를 반환합니다.
}
액션 값은 주로 다음과 같은 형태로 이루어져 있습니다,
{
type: 'INCREMENT',
}
후에 다뤄볼 리덕스
에서 액션 객체에는 어떤 액션인지 알려주는 type
필드가 꼭 있어야 하지만 useReducer에서 사용하는 액션 객체는 반드시 type
을 지니고 있을 필요가 없습니다.
useReducer를 사용하여 기존의 Counter 컴포넌트를 다시 구현해보세요.
Counter.js`
import { useReducer } from 'react';
function reducer(state, action){
// action.type에 따라 다른 작업 수행
switch(action.type){
case 'INCREMENT':
return { value : state.value + 1 };
case 'DECREMENT':
return { value : state.value - 1 };
default:
// 아무것도 해당되지 않을 때 기존 상태 반환
return state;
}
}
const Counter = () => {
// state의 기본값
const [state, dispatch] = useReducer(reducer, { value: 0 });
return (
<div>
<p>
현재 카운터 값은 <b>{state.value}입니다.</b>
</p>
<button onClick={() => dispatch({type:'INCREMENT'})}>+1</button>
<button onClick={() => dispatch({type:'DECREMENT'})}>-1</button>
</div>
);
};
export default Counter;
state는 현재 가리키고 있는 상태, dispatch는 액션을 발생시키는 함수입니다.
dispatch(action)과 같은 형태로, 함수 안에 파라미터로 액션 값을 넣어 주면 리듀서 함수가 호출되는 구조입니다.
useReducer를 사용했을 때의 가장 큰 장점은 컴포넌트 업데이트 로직을 컴포넌트 바깥으로 빼낼 수 있다는 것입니다.
보는 바와 같이 Counter 컴포넌트 바깥으로 reducer를 빼서 state를 관리할 수 있습니다.
useReducer
를 사용하여 Info 컴포넌트에서 인풋상태를 관리해 보겠습니다.
기존에는 인풋이 여러 개여서 useState
를 여러 번 사용했습니다.
useReducer
를 사용하면 기존에 클래스형 컴포넌트에서 input 태그에 name값을 할당하고 e.target.name을 참조하여 setState를 해준 것과 유사한 방식으로 작업을 처리할 수 있습니다
Info.js컴포넌트를 수정해 봅시다.
// Info.js
import { useReducer } from 'react';
function reducer(state, action){
return {
...state,
[action.name]: action.value
};
}
const Info = () => {
const [state, dispatch] = useReducer(reducer, {
name: '',
nickname: ''
});
const {name,nickname} = state;
const onChange = e => {
dispatch(e.target);
console.log(e.target);
};
return (
<div>
<div>
이름 : <input name="name" value={name} onChange={onChange}/>
<br/>
닉네임 : <input name="nickname" value={nickname} onChange={onChange}/>
</div>
<div>
<p>이름: <b>{name}</b></p>
<p>닉네임: <b>{nickname}</b></p>
</div>
</div>
);
};
export default Info;
제가 이해한 바에 따르면
function reducer(state, action){
return {
...state,
[action.name]: action.value
};
}
…state,로 기존의 name, nickname을 받아오고
받아온 state를 [action.name]란 이름으로 추적하여 action.value를 할당합니다.
즉, 받아온 state에서 추적할[action.name]목록을 할당받고,
할당받은 action.name의 action.value값을 추적하여 state를 업데이트합니다.