🥯 애피타이저
안녕하세요. useReducer에 대해 공부하는 김에 TodoList를 만들어보게 되었읍니다.
매우 간단하고 디자인이라곤 1도 찾아볼 수 없으니 감안하고 봐주세요.
혹여나 코드에 대해 피드백이 있으시다면 댓글로 알려주세요. 그렇다면 제가 무한 감사를 드릴 것입니다.
🍖 메인
TodoList의 얼굴
해야 할 일을 써넣을 input 태그와 등록 버튼을 담은 form, TodoItem 컴포넌트, TodoItem들을 담을 TodoList 컴포넌트, 이렇게 3개의 컴포넌트로 구성된 간단한 앱입니다.
reducer
// reducer 함수
const reducer = (state, action) => {
switch (action.type) {
case "INIT":
return [...action.payload];
case "CREATE":
return [action.payload, ...state];
case "REMOVE":
return state.filter((v) => v.id !== action.payload);
default:
return state;
}
};
reducer 함수입니다.
type으로 "INIT"이 들어오면 state를 payload로 전달된 데이터를 스프레드 연산자를 사용해서 새로운 배열로 만든 뒤 반환합니다.
type으로 "CREATE"가 들어오면 payload로 전달된 데이터와 기존 state를 스프레드 연산자를 사용해서 새로운 배열로 만든 뒤 반환합니다.
type으로 "REMOVE"가 들어오면 payload로 전달된 데이터의 id를 이용합니다.
filter 내장 함수를 사용해서 state의 데이터들 중 id와 같지 않은 것들만 찾아서 새로운 배열로 만든 뒤 반환합니다.
모두 공통점이 있는데, 바로 새로운 배열로 반환해야 한다는 것입니다.
왜냐하면 리액트의 state는 불변성을 지켜야 해서 기존 state가 변경이 되면 좋지 않습니다.
이유는 구글에 "리액트 state 불변성" 이라고만 쳐도 좌르륵 나오니 검색해 보시면 재밌습니다.
더미 데이터 받아오기
// axios를 이용해서 데이터 받아오기
const getData = async () => {
try {
const response = await axios.get(
"https://jsonplaceholder.typicode.com/todos"
);
const data = response.data;
dispatch({ type: "INIT", payload: data });
} catch (err) {
console.log(err);
}
};
axios 라이브러리를 활용해서 JSONPlaceholder에서 더미 데이터를 받아옵니다.
get 요청으로 데이터를 받아왔다면 dispatch 함수에 type을 INIT으로 설정하고 payload로 데이터를 넣어줍니다.
useEffect(() => {
getData();
}, []);
useEffect hook을 활용해서 최초 렌더링이 될 때 데이터를 받아올 수 있도록 했습니다.
비어 있는 의존성 배열에 todos가 들어가야 할 것 같은데 그냥 빈 배열입니다.
왜?
원래는 의존성 배열에 todos를 넣어야 하지만, 제가 받아와서 사용하고 있는 데이터는 수정과 삭제가 불가능하므로 제가 임의로 구현한 삭제 로직이 동작하지 않게 됩니다. 그래서 빈 배열로 넣어줬습니다 ㅎㅎ;
( 빈 배열로 넣으면 최초 렌더링 때에만 작동하고 작동하지 않습니다. )
onSubmit
const onSubmit = (e) => {
e.preventDefault();
dispatch({
type: "CREATE",
payload: {
id: new Date() + Math.random(),
title: todoInput.current.value
},
});
resetTodoInput();
};
const resetTodoInput = () => {
todoInput.current.value = "";
todoInput.current.focus();
};
등록 버튼을 누르면 Todo가 등록되는 onSubmit 함수입니다.
먼저 e.preventDefault로 새로고침을 막아줍니다.
dispatch의 type으로 CREATE를 넘겨주고 payload로 Todo객체를 넘겨줍니다.
이 객체에는 id와 Todo 내용이 들어갑니다.
Todo를 등록했다면 입력 칸의 내부를 비워주고 focus를 잡는 resetTodoInput 함수를 호출합니다.
그런데 Todo의 내용과 입력 칸을 비워주는 함수에서 todoInput이라는 녀석이 좀 이상합니다.
state라면 setState로 변경을 시켜줘야 하는데, 저 current.value나 current.focus는 뭐지? 하실 수 있습니다.
const todoInput = useRef();
바로 useRef 입니다.
원래는 onChange를 이용해서 input 값을 state에 저장하고 onClick으로 내용을 등록시켰었습니다.
그런데 onChange를 이용해서 state에 저장하면 state 값이 변경될 때마다 초기화가 돼서 재렌더링이 발생했습니다.
이 끝도 없는 재렌더링이 저는 너무나 불편해서 구글링을 하며 방법을 찾아보았습니다.
그 결과, useRef Hook을 이용하면 불필요한 재렌더링을 막을 수 있다는 것을 깨닫고 사용해버렸습니다.
onRemove
const onRemove = (id) => {
dispatch({ type: "REMOVE", payload: id });
};
Todo를 삭제해버리는 onRemove 함수 입니다.
type으로 REMOVE, payload로 Todo의 id를 넘겨줍니다.
컴포넌트 배치
return (
<div className="App">
<InputForm onSubmit={onSubmit} todoInput={todoInput} />
<TodoList todos={todos} onRemove={onRemove} />
</div>
);
InputForm 컴포넌트로 onSubmit 함수와 todoInput Ref 객체를 props로 넘겨줍니다.
TodoList 컴포넌트로 Todo배열들과 onRemove 함수를 넘겨줍니다.
컴포넌트 내부 코드들은 너무나 간단하므로 설명은 패스하도록 하겠읍니다!
자, 이제 매우 허접한 TodoList 만들기가 끝났습니다.
🍨 디저트
시연 영상
부족하고 또 부족한 글 읽어주셔서 감사합니다.
그래도 다음에 또 오세요.
'React > Hooks' 카테고리의 다른 글
useId를 간단하게 알아보자 (0) | 2023.08.15 |
---|---|
useMemo를 알아봅시다. (Feat. 예제) (0) | 2023.04.06 |
useReducer를 알아보자 (0) | 2023.03.23 |