728x90
반응형
Todo 앱 만들기
1. App 컴포넌트
- 최상위 컴포넌트인 App 에 todos state 를 정의해놓고 사용
- 각 todo 에는 id 가 존재하는데, id 는 ref 로 정의 -> ref 는 state 와 다르게 값이 변경되어도 렌더링 x
import './App.css'
import Header from './components/Header'
import Editor from './components/Editor'
import List from './components/List'
import { useState, useRef } from 'react'
const mockData = [
{
id: 0,
isDone: false,
content: "React 공부하기",
date: new Date().getTime(),
},
{
id: 1,
isDone: true,
content: "빨래하기",
date: new Date().getTime(),
},
{
id: 2,
isDone: false,
content: "밥먹기",
date: new Date().getTime(),
}
];
function App() {
const [todos, setTodos] = useState(mockData);
const idRef = useRef(3);
const onCreate = (content) => {
const newTodo = {
id: idRef.current++,
isDone: false,
content: content,
date: new Date().getTime()
}
// todos.push() 가 안되는 이유 -> state이기 때문에 상태변경함수로만 변경 가능
setTodos([newTodo, ...todos]);
}
const onUpdate = (targetId) => { // 토글 변경
setTodos(todos.map((todo) =>
todo.id === targetId ? {...todo, isDone: !todo.isDone} : todo
));
}
const onDelete = (targetId) => { // 삭제
setTodos(todos.filter((todo) =>
todo.id !== targetId
))
}
return (
<div className='App'>
<Header />
<Editor onCreate={onCreate} />
<List todos={todos} onUpdate={onUpdate} onDelete={onDelete} />
</div>
)
}
export default App
2. Editor 컴포넌트
- 부모 컴포넌트로부터 todo 를 추가할 수 있는 핸들러 onCreate 를 props 로 받아서 사용
- onSubmit 핸들러를 사용해서 input 태그에 내용이 있을 때와 없을 때를 분기 처리
import React, { useState, useRef } from 'react'
import './Editor.css'
const Editor = ({onCreate}) => {
const [content, setContent] = useState('');
const contentRef = useRef();
const onChangeContent = (e) => {
setContent(e.target.value);
}
const onKeyDown = (e) => { // 엔터쳐서 todo 추가
if (e.keyCode === 13) {
onSubmit();
}
}
const onSubmit = () => {
if (content) {
onCreate(content); // todo 추가
}
else {
contentRef.current.focus(); // 입력된 내용이 없다면 포커싱
return;
}
setContent('');
}
return (
<div className='Editor'>
<input
ref={contentRef}
type='text'
placeholder='새로운 Todo...'
value={content}
onChange={onChangeContent}
onKeyDown={onKeyDown}
/>
<button onClick={onSubmit}
>추가</button>
</div>
)
}
export default Editor
3. List 컴포넌트
- 부모 컴포넌트로부터 할일이 들어가있는 todos 배열, todo 토글함수(onUpdate), todo삭제 함수를 props 로 받아서 사용
- search state 로 검색 기능 구현 -> getFilteredData 함수로 찾는 값과 일치하는 todo 필터링한 후에 화면에 렌더링
import React, { useState } from 'react'
import './List.css'
import TodoItem from './TodoItem'
const List = ({todos, onUpdate, onDelete}) => {
const [search, setSearch] = useState("");
const onChangeSearch = (e) => {
setSearch(e.target.value);
};
const getFilteredData = () => {
if (search) {
return todos.filter((todo) =>
todo.content.toLowerCase().includes(search.toLowerCase()));
} else {
return todos;
}
};
const filteredTodos = getFilteredData();
return (
<div className='List'>
<h4>Todo List</h4>
<input
type='text'
placeholder='검색어를 입력하세요'
value={search}
onChange={onChangeSearch}
/>
<div className='todos-wrapper'>
{
filteredTodos.map(todo => {
return (<TodoItem key={todo.id} {...todo} onUpdate={onUpdate} onDelete={onDelete} /> );
})
}
</div>
</div>
)
}
export default List
4. TodoItem 컴포넌트
- 부모 컴포넌트로부터 todo 구성에 필요한 id, isDone, content, date 와 todo 토글 함수(onUpdate), todo 삭제 함수(onDelete)를 props 로 받아서 사용
import React from 'react'
import './TodoItem.css'
const TodoItem = ({id, isDone, content, date, onUpdate, onDelete}) => {
const onChangeCheckbox = () => {
onUpdate(id);
};
const onClickDeleteButton = () => {
onDelete(id);
};
return (
<div className='TodoItem'>
<input type='checkbox' onChange={onChangeCheckbox} checked={isDone}/>
<div className='content'>{content}</div>
<div className='date'>{new Date(date).toLocaleDateString()}</div>
<button
onClick={onClickDeleteButton}
>삭제</button>
</div>
)
}
export default TodoItem
현재 최상위 컴포넌트인 App 에서 정의하고 있는 onUpdate, onDelete 함수가 List 컴포넌트를 거쳐서 TodoItem 컴포넌트로 넘겨지고 있다. (List 컴포넌트에서는 onUpdate, onDelete 함수를 사용하지 않기 때문에 사실상 TodoItem 으로 전달을 위해 거쳐가는 셈)
현재는 볼륨이 작기 때문에 크게 크게 상관없지만, 컴포넌트 구조가 깊어질수록 props drilling 이 (필요한 데이터를 하위 컴포넌트에 전달하기 위해 중간 컴포넌트를 하나하나 거쳐서 props 를 전달하는 과정) 발생할 수 있기 때문에 다른 방법으로 props 를 전달하는 것을 권장한다! ( ex. Redux, React Context API 등)
728x90
반응형
'웹 > 24-StudyWithPnP' 카테고리의 다른 글
[React.js] 8주차 - MPA, SPA, 이미지 최적화 (1) | 2024.12.02 |
---|---|
[React.js] 7주차 - 성능 최적화: useMemo, useCallback, useContext, React.memo (1) | 2024.11.26 |
[React.js] 5주차 - Counter 만들기, useEffect (10) | 2024.11.04 |
[React.js] 4주차 - state, useState, useRef, React Hooks, 렌더링 (0) | 2024.10.29 |
[React.js] 3주차 - React.js 입문(1) (0) | 2024.10.14 |