useReducer
2023-09-17
ReactHooks
Table of Contents
useReducer가 나오게 된 배경
- 복잡한 React 애플리케이션에서 상태 관리를 더 구조적이고 예측 가능하게 만들고 복잡성을 줄이기 위한 도구로 등장했습니다.
1. 프로젝트 세팅 React + TS + Vite
$ npm init vite@latest React-UseReducer -- --template react-ts
$ cd React-UseReducer
$ npm i
2. useReducer 사용해보기
useReducer의 구조
const [state, dispatch] = useReducer(reducer, initialState, init?);
-
useReducer는 두개의 항목을 배열안에 리턴하는데 첫번째 항목은 값, 두번째 항목은 dispatch 함수를 리턴합니다.
-
useReducer의 첫번째 파라미터로 reducer함수와 두번째 파라미터는 초기화될 값, 그리고 마지막 파라미터로 Lazy Initialization을 optional로 지원합니다.
function init(initialState){ return initialState; }
/src/App.tsx
import { useReducer } from "react";
import "./App.css";
// action의 타입 정의
type Action = {
type: "Plus" | "Minus" | "Default";
};
// 리듀서 함수
function reducer(state: number, action: Action) {
console.log("state :: ", state, "action :: ", action);
switch (action.type) {
case "Plus":
return state + 1;
case "Minus":
return state - 1;
case "Default":
return 0;
default:
return state;
}
}
function App() {
const [state, dispatch] = useReducer(reducer, 0);
function plusClickHandler(event: React.MouseEvent<HTMLButtonElement>) {
event.preventDefault();
dispatch({ type: "Plus" });
}
function minusClickHandler(event: React.MouseEvent<HTMLButtonElement>) {
event.preventDefault();
dispatch({ type: "Minus" });
}
function defaultClickHandler(event: React.MouseEvent<HTMLButtonElement>) {
event.preventDefault();
dispatch({ type: "Default" });
}
return (
<div>
<button onClick={plusClickHandler}>Plus!</button>
<button onClick={minusClickHandler}>Minus!</button>
<button onClick={defaultClickHandler}>Default!</button>
<div>state :: {state}</div>
</div>
);
}
export default App;
- 버튼을 누를때마다 onClick시 할당한 함수가 실행됩니다.
- dispatch 함수가 할당된 reducer 함수를 실행시킵니다. reducer함수 에는 dispatch 함수가 보낸 현재 state와 인자로 보낸 객체가 담겨져 있습니다.
// action의 타입 정의
type Action = {
type: "Plus" | "Minus" | "Default";
};
// 리듀서 함수
function reducer(state: number, action: Action) {
console.log("state :: ", state, "action :: ", action);
switch (action.type) {
case "Plus":
return state + 1;
case "Minus":
return state - 1;
case "Default":
return 0;
default:
return state;
}
}
- 저희가 작성한 reducer 함수에서 action의 type의 값 별로 분기하는 로직을 작성하였습니다. reducer의 일반적인 패턴입니다.
- 결과론적으로, 현재 할당된 state 값과 같은 타입의 값을 return해야합니다.
업데이트 로직 분리하기(리듀서 함수의 모듈화)
src/reducer/AppReducer.ts
type Action = {
type: "Plus" | "Minus" | "Default";
};
// 리듀서 함수
function AppReducer(state: number, action: Action) {
console.log("state :: ", state, "action :: ", action);
switch (action.type) {
case "Plus":
return state + 1;
case "Minus":
return state - 1;
case "Default":
return 0;
default:
return state;
}
}
export { AppReducer };
- 간단한 Reducer를 리턴하는 파일을 만들었습니다.
/src/App.tsx
import { useReducer } from "react";
import { AppReducer as reducer } from "./reducer/AppReducer";
import "./App.css";
function App() {
const [state, dispatch] = useReducer(reducer, 0);
function plusClickHandler(event: React.MouseEvent<HTMLButtonElement>) {
event.preventDefault();
dispatch({ type: "Plus" });
}
function minusClickHandler(event: React.MouseEvent<HTMLButtonElement>) {
event.preventDefault();
dispatch({ type: "Minus" });
}
function defaultClickHandler(event: React.MouseEvent<HTMLButtonElement>) {
event.preventDefault();
dispatch({ type: "Default" });
}
return (
<div>
<button onClick={plusClickHandler}>Plus!</button>
<button onClick={minusClickHandler}>Minus!</button>
<button onClick={defaultClickHandler}>Default!</button>
<div>state :: {state}</div>
</div>
);
}
export default App;
- App.tsx를 다시 작성해보았습니다. 코드가 확실히 적어진 모습입니다.
3. useReducer의 타입
초기화
- useReducer의 제네릭타입은 Reducer타입을 상속받은 R과, I 타입의 제네릭을 받습니다.
- reducer: reducer 함수의 타입은 R타입입니다. R타입은 Reducer타입을 상속받고 있습니다.
- 타입을 보시면 Reducer란 타입은 함수형태이며, prevState에 S 타입과, action에 A타입을 받고 있고, S타입의 값을 리턴하고 있습니다. 요약하면, 이전값과 액션을 받아 현재할당된 State타입을 리턴하는 함수라고 요약할 수 있습니다.
- initializerArg: I 타입과
ReducerState<R>타입을 모두 만족하는 타입을 받고,ReducerState<R>타입을 할당합니다.
- ReducerState는 Reducer 타입의 제네릭 R을 할당받으며, R의 prevState가 S타입으로 확장하면 S로 할당하고 아니면 never를 할당합니다.
- initializer: 파라미터 arg는 I 타입과
ReducerState<R>타입을 모두 만족하는 타입을받고,ReducerState<R>을 리턴하는 함수입니다.
Return Values