728x90
💡useReducer
컴포넌트 내부에 새로운 State를 생성하는 React Hook
모든 useState는 useReducer로 대체 가능
상태 관리 코드를 컴포넌트 외부로 분리할 수 있음
import { useReducer } from "react";
// # 3번: 상태를 어떻게 변화시킬지 적용
// reducer : 변환기
// -> 상태를 실제로 변화시키는 변환기 역할
function reducer(state, action) {
// if (action.type === "INCREASE") {
// return state + action.data; // return만 해주면 값은 자동으로 state값으로 반영된다.
// } else if (action.type === "DECREASE") {
// return state - action.data;
// }
switch (action.type) {
case "INCREASE":
return state + action.data;
case "DECREASE":
return state - action.data;
default:
return state;
}
}
const Exam = () => {
// dispatch : 발송하다, 급송하다
// -> 상태 변화가 있어야 한다는 사실을 알리는, 발송하는 함수
const [state, dispatch] = useReducer(reducer, 0); // # 1번: useReducer 생성
// 컴포넌트 내부에서 dispatch함수를 호출하게 되면, 상태 변화가 요청이 되고,
// useReducer는 상태 변화를 실제로 처리하게 될 함수를 호출한다.
// 상태 변화를 실제로 처리하게 될 함수는 직접 만들어야 한다.
// useReducer(상태 변화를 실제로 처리하게 될 함수, state의 초기값)
const onClickPlus = () => {
// # 2번: dispatch의 액션 명시
// 인수 : 상태가 어떻게 변화되길 원하는지
// -> 액션 객체체
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;
9.2) 투두리스트 업그레이드
useReducer 활용
todos처럼 배열 안에 객체가 들어가는 복잡한 구조는 보통 useReducer를 이용해서 관리하는게 일반적이다.
카운터 앱처럼 간단한 상태 변화 코드만 있다면 useState를 사용해도 충분하다.
주석없는 버전
더보기
import { useReducer, useRef, useState } from "react";
import "./App.css";
import Editor from "./components/Editor";
import Header from "./components/Header";
import List from "./components/List";
const mockData = [
{
id: 0,
isDone: false,
content: "React 공부하기",
date: new Date().getTime(),
},
{
id: 1,
isDone: false,
content: "빨래하기",
date: new Date().getTime(),
},
{
id: 2,
isDone: false,
content: "노래 연습하기",
date: new Date().getTime(),
},
];
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, setTodos] = useState(mockData);
const [todos, dispatch] = useReducer(reducer, mockData);
const idRef = useRef(3);
const onCreate = (content) => {
dispatch({
type: "CREATE",
data: {
id: idRef.current++,
isDone: false,
content: content,
date: new Date().getTime(),
},
});
// const newTodo = {
// id: idRef.current++,
// isDone: false,
// content: content,
// date: new Date().getTime(),
// };
// setTodos([newTodo, ...todos]);
};
const onUpdate = (targetId) => {
// todos State의 값들 중에
// targetId와 일치하는 id를 갖는 투두 아이템의 isDone 변경
// 인수: todos 배열에서 targetId와 일치하는 id를 갖는 요소의 데이터만 딱 바꾼 새로운 배열
// setTodos(
// todos.map((todo) =>
// todo.id === targetId ? { ...todo, isDone: !todo.isDone } : todo
// )
// );
dispatch({
type: "UPDATE",
targetId: targetId,
});
};
const onDelete = (targetId) => {
// 인수: todos 배열에서 targetId와 일치하는 id를 갖는 요소만 삭제한 새로운 배열
// setTodos(todos.filter((todo) => todo.id !== targetId));
dispatch({
type: "DELETE",
targetId: targetId,
});
};
return (
<div className="App">
{/* <Exam></Exam> */}
<Header />
<Editor onCreate={onCreate} />
<List todos={todos} onUpdate={onUpdate} onDelete={onDelete} />
</div>
);
}
export default App;
주석있는 버전
더보기
import { useReducer, useRef, useState } from "react";
import "./App.css";
import Editor from "./components/Editor";
import Header from "./components/Header";
import List from "./components/List";
const mockData = [
{
id: 0,
isDone: false,
content: "React 공부하기",
date: new Date().getTime(),
},
{
id: 1,
isDone: false,
content: "빨래하기",
date: new Date().getTime(),
},
{
id: 2,
isDone: false,
content: "노래 연습하기",
date: new Date().getTime(),
},
];
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, setTodos] = useState(mockData);
const [todos, dispatch] = useReducer(reducer, mockData);
const idRef = useRef(3);
const onCreate = (content) => {
dispatch({
type: "CREATE",
data: {
id: idRef.current++,
isDone: false,
content: content,
date: new Date().getTime(),
},
});
// const newTodo = {
// id: idRef.current++,
// isDone: false,
// content: content,
// date: new Date().getTime(),
// };
// setTodos([newTodo, ...todos]);
};
const onUpdate = (targetId) => {
// todos State의 값들 중에
// targetId와 일치하는 id를 갖는 투두 아이템의 isDone 변경
// 인수: todos 배열에서 targetId와 일치하는 id를 갖는 요소의 데이터만 딱 바꾼 새로운 배열
// setTodos(
// todos.map((todo) =>
// todo.id === targetId ? { ...todo, isDone: !todo.isDone } : todo
// )
// );
dispatch({
type: "UPDATE",
targetId: targetId,
});
};
const onDelete = (targetId) => {
// 인수: todos 배열에서 targetId와 일치하는 id를 갖는 요소만 삭제한 새로운 배열
// setTodos(todos.filter((todo) => todo.id !== targetId));
dispatch({
type: "DELETE",
targetId: targetId,
});
};
return (
<div className="App">
{/* <Exam></Exam> */}
<Header />
<Editor onCreate={onCreate} />
<List todos={todos} onUpdate={onUpdate} onDelete={onDelete} />
</div>
);
}
export default App;
'공부 > frontend' 카테고리의 다른 글
React Hook 최적화: useMemo, memo, useCallback (1) | 2025.02.06 |
---|---|
라이프 사이클(useEffect) (1) | 2025.01.31 |
React 프로젝트 1.카운터 앱 (0) | 2025.01.30 |
React.js 입문 (0) | 2025.01.30 |
React.js 개론 (1) | 2025.01.30 |