Prop Drilling
을 해결하기 위해 Redux를 사용한다!Context API
: React 내장 API
Redux
: Third Party 라이브러리, 개발 편의를 위한 미들웨어 기능을 제공하고 성능 최적화를 제공한다.
Context API의 구조와 다르게 Redux는 조금 더 복잡하다.
❗ 하지만 소규모 프로젝트에는 효율성의 문제로 인해 Redux보다 Context API가 더 좋을 수도 있다.
💡 Redux를 프로젝트에 사용하기 위해서 반드시 터미널 명령어를 통한 다운로드가 필요하다.
yarn add redux react-redux
yarn add redux-logger
yarn add -D @types/redux-logger // 타입을 따로 설치해야함
yarn add -D redux-devtools-extension
yarn add redux-persist
🔷 이전에 자바스크립트와 리액트로 제작한 Todolist를 타입스크립트와 리덕스를 이용하여 리팩토링 하였다.
리덕스 사용 이전엔 Context API를 이용하였으나 redux로 리팩토링 하면서 contexts는 필요 없게 되었다.
useLocalStorage
훅도 redux-presist
를 사용하면서 필요 없게 되었다.
타입스크립트를 적용하면서 컴포넌트에 인터페이스를 새로 적용하였다. 그 외엔 컴포넌트에 큰 변화가 없다.
그래서 이번 리팩토링에서 가장 중요하다고 생각하는 redux 폴더의 파일들만 코드 리뷰를 진행한다.
💻 src/redux/tasks/actions.ts
import { v4 } from "uuid"
import { Action } from "./types"
export const addTask = (content: string): Action => {
return {
type: 'ADD_TASK',
payload: {
id: v4(),
content,
complete: false
},
};
};
export const updateTask = (
id: string,
content: string,
complete: boolean
): Action => {
return {
type: 'UPDATE_TASK',
payload: {
id,
content,
complete
}
}
};
export const removeTask = (id: string): Action => {
return {
type: 'REMOVE_TASK',
payload: {
id,
content: '',
complete: false
},
};
};
💻 src/redux/tasks/reducer.ts
import { Action, Task } from "./types"
export const tasks = (state: Task[] = [], action: Action) => {
switch (action.type) {
case 'ADD_TASK': {
const newTask = action.payload;
return [...state, newTask];
}
case "UPDATE_TASK": {
const updatedTask = action.payload;
return state.map(oldTask => oldTask.id === updatedTask.id ? updatedTask : oldTask);
}
case "REMOVE_TASK": {
const removedTask = action.payload;
return state.filter(task => task.id !== removedTask.id);
}
default: {
return state;
}
}
};
💻 src/redux/tasks/types.ts
export interface Task {
id: string;
content: string;
complete: boolean;
}
export type ActionType = 'ADD_TASK' | 'UPDATE_TASK' | 'REMOVE_TASK';
export type Action = { type: ActionType, payload: Task };
💻 src/redux/tasks/index.ts
export * from './actions'
export * from './reducer'
💻 src/redux/index.ts
import { applyMiddleware, combineReducers, createStore } from "redux";
import { tasks } from "./tasks";
import logger from "redux-logger"
import { composeWithDevTools } from "redux-devtools-extension";
import storage from "redux-persist"
import session from "redux-persist/lib/storage/session"
import { persistReducer } from "redux-persist";
import persistStore from "redux-persist/es/persistStore";
const persistConfig = {
key: 'root',
storage: session,
whitelist: ['tasks'],
};
const combinedReducer = combineReducers({ tasks });
const rootReducer = persistReducer(persistConfig, combinedReducer)
export const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(logger)));
export const persistor = persistStore(store as any);
export type RootState = ReturnType<typeof rootReducer>;
❗
createStore
의 경우deprecated
로 표시되는데 이는 추후에 다룰redux-toolkit
의configureStore
라는 상위호환이 있기 때문이다.
🖨 리팩토링 결과
💡
redux-devtools-extension
의 경우, 이벤트 진행 상황을 뒤로 돌리거나 현재까지의 진행 상황을 일정 시점부터 다시 자동으로 진행시키는 등의 작업 역시 가능하다. 세번째 사진 하단 재생바가 그 기능이다.
Redux와 타입스크립트를 이용해 Todolist 리팩토링에 성공하였다.