Flux 패턴에서 자주 쓰이는 reducer, dispatch 등을 useReducer
를 사용해서 알아보겠습니다.
먼저 counter
컴포넌트를 만들어 보겠습니다.
import { useState } from "react";
import Button from "../components/Button";
import Panel from "../components/Panel";
function CounterPage() {
const [count, setCount] = useState(10);
const [valueToAdd, setValueToAdd] = useState(0);
const increment = () => {
setCount(count + 1);
};
const decrement = () => {
setCount(count - 1);
};
const reset = () => {
setCount(0);
};
const handleChange = (e) => {
const value = parseInt(e.target.value) || 0;
setValueToAdd(+value);
};
const handleSubmit = (e) => {
e.preventDefault();
setCount(count + valueToAdd);
setValueToAdd(0);
};
return (
<Panel className="m-3">
<h1 className="text-3xl py-6 ">
Count is{" "}
<span className="text-5xl font-bold text-indigo-500">{count}</span>
</h1>
<div className="flex flex-row">
<Button onClick={increment}>Increment</Button>
<Button onClick={decrement}>Decrement</Button>
<Button onClick={reset}>reset</Button>
</div>
<form onSubmit={handleSubmit}>
<label>Add a lot!</label>
<input
value={valueToAdd}
onChange={handleChange}
type="number"
className="p-1 m-3 bg-gray-50 border border-gray-300"
/>
<Button>Add it!</Button>
</form>
</Panel>
);
}
export default CounterPage;
useState 로 만들어진 컴포넌트를 useReducer
를 사용해서 리팩토링 해보겠습니다.
import { useReducer } from "react";
import Button from "../components/Button";
import Panel from "../components/Panel";
import produce from "immer";
const INCREMENT_COUNT = "increment";
const DECREMENT_COUNT = "decrement";
const RESET = "reset";
const SET_VALUE_TO_ADD = "change_value_to_add";
const ADD_VALUE_TO_COUNT = "add_value_to_count";
const reducer = (state, action) => {
switch (action.type) {
case INCREMENT_COUNT:
state.count += 1;
return;
case DECREMENT_COUNT:
state.count -= 1;
return;
case RESET:
state.count = 0;
return;
case SET_VALUE_TO_ADD:
state.valueToAdd = action.payload;
return;
case ADD_VALUE_TO_COUNT:
state.count = state.count + state.valueToAdd;
state.valueToAdd = 0;
return;
default:
return;
}
};
function CounterPage() {
const [state, dispatch] = useReducer(produce(reducer), {
count: 0,
valueToAdd: 0,
});
const increment = () => dispatch({ type: INCREMENT_COUNT });
const decrement = () => dispatch({ type: DECREMENT_COUNT });
const reset = () => dispatch({ type: RESET });
const handleChange = (e) => {
const value = parseInt(e.target.value) || 0;
dispatch({ type: SET_VALUE_TO_ADD, payload: value });
};
const handleSubmit = (e) => {
dispatch({ type: ADD_VALUE_TO_COUNT });
e.preventDefault();
};
return (
<Panel className="m-3">
<h1 className="text-3xl py-6 ">
Count is{" "}
<span className="text-5xl font-bold text-indigo-500">
{state.count}
</span>
</h1>
<div className="flex flex-row">
<Button onClick={increment}>Increment</Button>
<Button onClick={decrement}>Decrement</Button>
<Button onClick={reset}>reset</Button>
</div>
<form onSubmit={handleSubmit}>
<label>Add a lot!</label>
<input
value={state.valueToAdd}
onChange={handleChange}
type="number"
className="p-1 m-3 bg-gray-50 border border-gray-300"
/>
<Button>Add it!</Button>
</form>
</Panel>
);
}
export default CounterPage;
실행결과는 완벽하게 동일하지만, 이전에 배웠던 contextApi
와 함께 사용하면, redux 와 비슷한 설정이 가능합니다.
useReducer
와 contextApi
에 대해서 이해했다면, redux
또한 무리없이 이해할 수 있습니다.