이번 글은 위에서 만들었던 투두리스트 프로젝트에서 기능을 추가하는 방법에 대해 작성하겠습니다.
기존 투두리스트에선 useState를 이용해 상태관리를 했었습니다.
이렇게 상태관리를 한다면 프로젝트 규모가 조금만 커져도 props drilling이 많이 일어날 것이고 코드 생산성, 가독성에서 불리할 것입니다.
그래서 대부분 대규모 프로젝트에선 redux라는 라이브러리를 사용해 전역적으로 상태관리를 진행하는데요.
redux에 대해 모른다면 아래글을 읽어주세요.
기존 투두리스트에서 redux를 적용하는 순서는 다음과 같습니다.
1. redux, react-redux, redux-persist설치
2. duks패턴으로 modues를 만들어준다
3. 기존에 사용한 useState와 localStorage를 지워준다.
4. redux를 적용해 전역적으로 상태관리를 해준다.
1. redux, react-redux, redux-persist설치
yarn add redux -D @types/redux
yarn add react-redux -D @types/react-redux
yarn add redux-persist -D @types/redux-persist
라이브러리를 설치해주세요.
2. duks패턴으로 modues를 만들어준다
src폴더에 modules라는 폴더를 만들고 index.ts와 itemReducer.ts 파일을 만들어 줬습니다.
./src/modules/index.ts
import { combineReducers } from "redux";
import itemReducer from "./itemReducer";
import storage from "redux-persist/lib/storage";
import { persistReducer } from "redux-persist";
const persistConfig = {
key: "todo", // localStorage key
storage, // localStorage
};
const rootReducer = combineReducers({
itemReducer,
});
export default persistReducer(persistConfig, rootReducer);
export type RootState = ReturnType<typeof rootReducer>;
./src/modules/itemReducer.ts
import { toUnicode } from "punycode";
import { Item } from "../types/type";
const ADD = "itemReducer/ADD" as const;
const DELETE = "itemReducer/DELETE" as const;
const CLEAR = "itemReducer/CLEAR" as const;
// const initialState = JSON.parse(localStorage.getItem("todos") || "");
// 액션 생성함수를 선언합니다
export const addItem = (id: string, name: string, clear: boolean) => ({
type: ADD,
payload: { id, name, clear },
});
export const deleteItem = (id: string) => ({
type: DELETE,
payload: id,
});
export const clearItem = (id: string) => ({
type: CLEAR,
payload: id,
});
type TodoAction =
| ReturnType<typeof addItem>
| ReturnType<typeof deleteItem>
| ReturnType<typeof clearItem>;
type TodoState = {
todo: Item[];
};
const initialTodo = {
todo: [],
};
const itemReducer = (
state: TodoState = initialTodo,
action: TodoAction
): TodoState => {
switch (action.type) {
case ADD:
return {
todo: state.todo.concat({
itemId: action.payload.id,
itemName: action.payload.name,
clear: action.payload.clear,
}),
};
case DELETE:
return {
todo: state.todo.filter((todo) => {
return todo.itemId === action.payload ? false : true;
}),
};
case CLEAR:
return {
todo: state.todo.map((todo) => {
return todo.itemId === action.payload
? { ...todo, clear: !todo.clear }
: todo;
}),
};
default:
return state;
}
};
export default itemReducer;
redux-persist는 localStorage에 접근하여 초기값을 저장해주는 라이브러리입니다.
위와 같이 리듀서를 만들어 주겠습니다.
3. 기존에 사용한 useState와 localStorage를 지워준다.
aApp.tsx와 InsertItem.tsx,ItemList.tsx 파일에 있는 모든 useState와 props들을 지워줍니다.
./src/App.tsx
./src/components/InsertItem.tsx
./src/components/ItemList.tsx
모든 porps들을 삭제해줍니다.
이후 이 porps들을 전역 상태로 변경해줍니다.
4. redux를 적용해 전역적으로 상태관리를 해준다.
./src/index.tsx
전역적으로 Provider와 PersistGate 태그를 묶어줍니다.
modules폴더에 만든 reducer를 불러온 후 store로 만들어줍니다.
store를 localStorage에 저장하기 위해 persistStore를 이용해 persistor를 만들어줍니다.
Provider태그에 store를 선언해준다.
PersistGate태그에 persistor를 선언해준다.
redux를 사용할 준비는 모두 마쳤습니다.
이제 각 컴포넌트에 todolist가 필요하다면 selector를 통해 todo목록을 가져와 주거나 dispatch를 통해 todo목록을 수정해주면 됩니다.
./src/components/InsertItem.tsx
import { useState } from "react";
import { v4 as uuid } from "uuid";
import { addItem } from "../modules/itemReducer";
import { useDispatch } from "react-redux";
const InsertItem = () => {
const [itemName, setItemName] = useState(""); // 이 useState는 inputbox에 있는 텍스트를 동기화시켜주는 변수이기에 남겨줍니다.
const dispatch = useDispatch();
const changeItemName = (e: React.ChangeEvent<HTMLInputElement>) => {
setItemName(e.target.value);
};
const clickButton = (e: React.MouseEvent<HTMLButtonElement>) => {
const itemId = uuid();
dispatch(addItem(itemId, itemName, false));
setItemName("");
console.log(itemName);
};
return (
<>
<input
className="task-input"
value={itemName}
onChange={changeItemName}
type="text"
/>
<button className="button-add" onClick={clickButton}>{`생성`}</button>
</>
);
};
export default InsertItem;
./src/components/ItemList.tsx
import { Item } from "../types/type";
import { RootState } from "../modules";
import { useSelector, useDispatch } from "react-redux";
import { deleteItem, clearItem } from "../modules/itemReducer";
const ItemList = () => {
const todo = useSelector((state: RootState) => state.itemReducer.todo);
const dispatch = useDispatch();
const clickDelete = (data: Item) => {
dispatch(deleteItem(data.itemId));
};
const clickComplete = (data: Item) => {
dispatch(clearItem(data.itemId));
};
return (
<ul>
{todo.map((data: Item) => {
return (
<li className="list-item" key={data.itemId}>
<p className={`${data.clear ? "complete" : ""}`}>{data.itemName}</p>
<div>
<button
className="button-delete"
onClick={() => clickDelete(data)}
>{`삭제`}</button>
<button
className="button-complete"
onClick={() => clickComplete(data)}
>{`해결`}</button>
</div>
</li>
);
})}
</ul>
);
};
export default ItemList;
위 코드처럼 기존에 사용했던 porps들을 selector로 바꿔주고 button들의 기능엔 dispatch를 이용해 todo목록을 변경해줍니다.
이제 todo의 아이템들을 전역적으로 상태관리 할 수 있게 되었습니다.
또 redux-persist를 이용해 localStorage에 todolist를 저장할 수 있습니다.
브라우저를 새로고침하거나 브라우저를 닫고 다시 프로젝트를 실행해도 투두 목록이 남아있게 됩니다.
'프로젝트' 카테고리의 다른 글
Figma -> React 프로젝트 대쉬보드 퍼블리싱 (0) | 2022.12.12 |
---|---|
Figma -> React 프로젝트 원페이지 사이트 홈 퍼블리싱 (0) | 2022.12.12 |
리그오브레전드 팀나누기 프로젝트 (0) | 2022.12.10 |
포트폴리오 웹사이트 프로젝트 (0) | 2022.12.10 |
[리액트 따라하며 배우기] 달력 프로젝트2 달력에 공휴일 표시하기 React+typescript, axios (0) | 2022.12.08 |