useReducer
1. useReducer
- useReducer란?
- 컴포넌트 내부에 새로운 State를 생성하는 React Hook
- 모든 useState는 useReducer로 대체 가능
- useState와 useReducer의 차이점?
- useState는 컴포넌트 내부에서만 관리가 가능 하지만 useReducer는 상태 관리 코드를 컴포넌트 외부로 분리하여 관리 할 수 있는 핵심적인 차이점이 있다
- 컴포넌트의 메인 역할을 UI를 렌더링하는 것이지만 상태관리코드가 길어진다면 주객전도 되는 것
2. useReducer 활용법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// useState 예시
import useState from 'react';
function App() {
const [todos, setTodos] = useState([]);
const idRef = useRef(0);
// 컴포넌트 내부에 상태 관리 코드를 작성해야함
const onCreate = (content) => {
const newTodo = {
id: idRef.current++
, isDone: false
, content: content
, date: new Date().getTime()
};
setTodos([newTodos, ...todos]);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// useReducer 예시
// const [state, dispatch] = useReducer(reducer, initialState);
// state: 상태
// dispatch: 상태를 변화시키는 action을 발생시키는 함수
// reducer: dispatch로 인해 일어난 상태변화 처리 함수
// initialState: 초기값
import { useReducer } from "react";
// reducer : 변환기
// -> 상태를 실제로 변환시키는 변환기
function reducer(state, action) {
console.log(state, action);
// if (action.type === "INCREASE") {
// return state + action.data;
// } else if (action.type === "DECRESE") {
// return state - action.data;
// }
// 액션의 타입이 많아질 경우, switch문 사용이 일반적임
switch (action.type) {
case "INCREASE" : return state + action.data;
case "DECREASE" : return state - action.data;
default : state;
}
}
const Exam = () => {
// dispatch : 발송하다, 급송하다
// -> 상태 변화가 있어야 한다는 사실을 알리는, 발송하는 함수
const [state, dispatch] = useReducer(reducer, 0);
const onClickPlus = () => {
// 인수: 상태가 어떻게 변화되길 원하는지
// -> 액션 객체
dispatch({
type: "INCREASE"
, data: 1
});
}
const onClickMinus = () => {
dispatch({
type: "DECREASE"
, data: 1
})
}
return (
<div>
<h1>{state}</h1>
<button onClick={onClickPlus}>+</button>
<button onClick={onClickMinus}>-</button>
</div>
);
}
export default Exam;
3. 투두리스트 업그레이드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 수정 전 App.jsx
import './App.css'
import { useState, useRef } from "react";
import Header from "./components/Header";
import Editor from "./components/Editor";
import List from "./components/List";
function App() {
const [todos, setTodos] = useState([]);
const idRef = useRef(0);
const onCreate = (content) => {
const newTodo = {
id: idRef.current++
, isDone: false
, content: content
, date: new Date().getTime()
};
setTodos([newTodo, ...todos]);
}
const onUpdate = (targetId) => {
setTodos(todos.map((todo) => todo.id === targetId ? { ...todo, isDone: !todo.isDone} : todo))
}
const onDelete = (targetId) => {
setTodos(todos.filter((todo) => todo.id !== targetId));
}
return (
<div className='App'>
<Header />
<Editor onCreate={onCreate} />
<List todos={todos} onUpdate={onUpdate} onDelete={onDelete} />
</div>
);
}
export default App
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// 수정 후 App.jsx
import './App.css'
import { useState, useRef, useReducer } from "react";
import Header from "./components/Header";
import Editor from "./components/Editor";
import List from "./components/List";
function reducer(state, action) {
switch (action.type) {
case "CREATE" : return [action.data, ...state];
case "UPDATE" : return state.map(item => item.id === action.targetId ? {...item, isDone: !item.isDone} : item);
case "DELETE" : return state.filter(item => item.id != action.targetId);
default: return state;
}
}
function App() {
const [todos, dispatch] = useReducer(reducer, []);
const idRef = useRef(0);
const onCreate = (content) => {
dispatch({
type: "CREATE"
, data: {
id: idRef.current++
, isDone: false
, content: content
, date: new Date().getTime()
}
})
}
const onUpdate = (targetId) => {
dispatch({
type: "UPDATE"
, targetId: targetId
})
}
const onDelete = (targetId) => {
dispatch({
type: "DELETE"
, targetId: targetId
})
}
return (
<div className='App'>
<Header />
<Editor onCreate={onCreate} />
<List todos={todos} onUpdate={onUpdate} onDelete={onDelete} />
</div>
);
}
export default App