예전에는 keyPressEnter가 있었는데 deprecated됐다고 한다..그래서 keydown 이벤트를 추가하는 방법을 사용하면 된다고 한다. 그리고 keyCode도 지금은 안된다!
const handleKeypress = (e: React.KeyboardEvent<HTMLInputElement>) =>{
let key = e.key || e.keyCode;
if (key === 'Enter' || key === 13) {
addTodoList();
}
}
갑자기 toolkit을 사용해보고 싶어서 리덕스구조를 다시 만들었다.
│ App.tsx
│ index.css
│ index.tsx
│
├─components
│ TodoInput.tsx
│ TodoList.tsx
│ TodoStateBtns.tsx
│
└─Redux
│ types.ts
│
├─Actions
│ todolist_action.ts
│
└─Reducers
index.ts
todoReducer.ts
│ App.tsx
│ index.css
│ index.tsx
│
├─components
│ TodoInput.tsx
│ TodoList.tsx
│ TodoStateBtns.tsx
│
└─store
index.tsx
todoSlice.tsx
우선 Action과 reducer, type을 위한 폴더를 따로 구성하지 않고 하나의 Slice파일로 만들어서 복잡하지 않은게 최대 장점이다!
// todoSlice.tsx
import { createSlice } from "@reduxjs/toolkit";
export type TodoType = {
todoID: string;
todoContent: string;
checked: boolean;
}
type InitialStateType = TodoType[];
const initialState : InitialStateType = [];
const todoSlice = createSlice({
name : "todo",
initialState,
reducers : {
addTodo : (state, action) =>{
return [...state, action.payload];
},
removeTodo : (state,action) =>{
return state?.filter((todo) => todo.todoID !== action.payload);
},
setTodo : (state, action) =>{
const newState = [...state, ...action.payload]
return newState;
}
}
})
export default todoSlice;
export const {addTodo, removeTodo, setTodo} = todoSlice.actions;
createSlice를 통해서 이름과 초기상태, 리듀서를 한번에 정의할 수 있고 리듀서 선언도 간단해졌다.
const deleteList = ()=>{
let localStorageArr : Array<TodoType> = JSON.parse(localStorage.getItem('todos')!);
let idx = localStorageArr.filter((todo) => todo.todoID !== todoID);
dispatch(removeTodo(todoID));
localStorage.setItem('todos', JSON.stringify(idx));
}
const handleCheckList = () =>{
let localStorageArr : Array<TodoType> = JSON.parse(localStorage.getItem('todos')!);
localStorageArr.forEach((list)=>{
if(list.todoID === todoID) {
let idx = !list.checked;
list.checked = idx;
return;
}
})
dispatch(setTodo(localStorageArr));
localStorage.setItem('todos', JSON.stringify(localStorageArr));
}
기본 상태는 Doing으로 지금 하는 중인 투두를 나타내고, Done을 클릭하면 이미 완료한 투두를 보여준다
// App.tsx
const [btnState, setBtnState] = useState<string>('Doing');
function onSetList(btn : string){
let localStorageArr : Array<TodoType> = JSON.parse(localStorage.getItem('todos')!); //1. 로컬에서 가져오기
if ( localStorageArr!= null && btn === "Doing") {
let listArr = localStorageArr.filter((list)=> list.checked === false);
dispatch(setTodo(listArr));
}
else if(localStorageArr!= null && btn === "Done"){
let listArr = localStorageArr.filter((list)=> list.checked !== false);
dispatch(setTodo(listArr));
}
}
useEffect(()=>{
onSetList(btnState);
},[btnState]);
App.tsx에 btnState라는 상태값이 있고 기본적으로 Doing이며, 값에 따라서 다른 listArr을 만들어 리덕스에 상태를 관리해준다.
btnState상태값은 TodoList와 TodoStateBtns라는 컴포넌트들에 props로 전달된다
//TodoStateBtns.tsx
const doingBtnRef = useRef<HTMLButtonElement>(null);
const doneBtnRef = useRef<HTMLButtonElement>(null);
useEffect(()=>{
if(btnState === "Doing"){
doingBtnRef.current?.classList.add('bg-yellow-200');
doneBtnRef.current?.classList.remove('bg-yellow-200');
}else{
doneBtnRef.current?.classList.add('bg-yellow-200');
doingBtnRef.current?.classList.remove('bg-yellow-200');
}
},[btnState])
const handleBtnClick = (e : React.MouseEvent<HTMLElement>)=>{
const {name}= e.target as HTMLButtonElement;
if(name === 'doingBtn'){
doingBtnRef.current?.classList.add('bg-yellow-200');
doneBtnRef.current?.classList.remove('bg-yellow-200');
setBtnState('Doing');
}else{
doneBtnRef.current?.classList.add('bg-yellow-200');
doingBtnRef.current?.classList.remove('bg-yellow-200');
setBtnState('Done');
}
}
// TodoList.tsx
const handleCheckList = () =>{
let localStorageArr : Array<TodoType> = JSON.parse(localStorage.getItem('todos')!);
localStorageArr.forEach((list)=>{
if(list.todoID === todoID) {
let idx = !list.checked;
list.checked = idx;
if(idx === true){ //true로 바뀌면 Done으로 바꿔주기
setBtnState("Done");
}else{
setBtnState("Doing");
}
return;
}
})
dispatch(setTodo(localStorageArr));
localStorage.setItem('todos', JSON.stringify(localStorageArr));
}
간단한 todolist를 React+Redux(toolkit)+Typescript+TailwindCSS로 만들어 봤다. 확실히 css파일이 따로 없으니까 깔끔하고 부트스트랩보다 커스텀이 자유로워서 좋았다. 자주 사용할 듯 싶다(특히 그리드나 레이아웃 잡을때 편할 것 같다).
그리고 toolkit도 중간에 갑자기 사용하게 되었는데 action이나 reducer같은 폴더를 따로 안만들고 createSlice로 모든 설정이 가능한게 편리해서 앞으로는 꼭 toolkit을 사용할 것 같다.
Typescript는 사용하는데 익숙치가 않아서 아직 나에게는 에러를 많이 발생시키는 아이..지만 이제 더 복잡한 로직을 짜게된다면 분명 이점이 있을것이라 생각하고 더 공부해야겠다.