npm create react-app todo-app
npm install sass
npm install classnames
npm install react-icons
TodoTemplate.js
import React from 'react';
import '../styles/TodoTemplate.scss'
const TodoTemplate = ({children}) => {
return (
<div className='TodoTemplate'>
<div className='app-title'>🤍Schedule Management🤍</div>
<div className='content'>{children}</div>
</div>
);
};
export default TodoTemplate;
TodoTemplate.scss
.TodoTemplate {
width: 512px;
margin-left: auto;
margin-right: auto;
margin-top: 6rem;
border-radius: 4px;
overflow: hidden;
.app-title {
background-color: #b778ee;
color: white;
height: 4rem;
font-size: 1.5rem;
display: flex;
align-items: center;
justify-content: center;
}
.content {
background-color: white;
}
}
TodoInsert.js
import React, { useCallback, useState } from 'react';
import {MdAdd} from 'react-icons/md';
import '../styles/TodoInsert.scss';
const TodoInsert = ({onInsert}) => {
const [value, setValue] = useState('');
const changeValue = useCallback(e => {
setValue(e.target.value);
}, []); // callback when the first rendering
const handleSubmit = useCallback(e => {
onInsert(value);
setValue('');
// defending re-load
e.preventDefault();
}, [onInsert, value]);
return (
<form className='TodoInsert' onSubmit={handleSubmit}>
<input
type="text"
placeholder='Enter the Task'
value={value}
onChange={changeValue}/>
<button type='submit'>
<MdAdd/>
</button>
</form>
);
};
export default TodoInsert;
TodoInsert.scss
.TodoInsert {
display: flex;
background-color: #514957;
input {
background: none;
outline: none;
border: none;
padding: 0.5rem;
font-size: 1.125rem;
line-height: 1.5;
color: white;
&::placeholder {
color: #e3dee6;
}
// every area except for button
flex: 1;
}
button {
background: #968296;
outline: none;
border: none;
color: white;
padding-left: 1rem;
padding-right: 1rem;
font-size: 1.5rem;
display: flex;
align-items: center;
cursor: pointer;
transition: 0.1s background ease-in;
&:hover {
background: #b7adbd;
}
}
}
TodoListItem.js
import React from 'react';
import {
MdCheckBoxOutlineBlank,
MdCheckBox,
MdRemoveCircleOutline
} from 'react-icons/md';
import '../styles/TodoListItem.scss';
import cn from 'classnames';
const TodoListItem = ({todo, onRemove, onChecked}) => {
const {id, text, checked} = todo;
return (
<div className='TodoListItem'>
<div className={cn('checkbox', {checked})} onClick={() => onChecked(id)}>
{checked ? <MdCheckBox/> : <MdCheckBoxOutlineBlank/>}
<div className='text'>{text}</div>
</div>
<div className='remove' onClick={() => onRemove(id)}>
<MdRemoveCircleOutline/>
</div>
</div>
);
};
export default TodoListItem;
TodoListItem.scss
.TodoListItem {
padding: 1rem;
display: flex;
align-items: center;
&:nth-child(even) {
background: #f9f8fa;
}
.checkbox {
cursor: pointer;
flex: 1;
display: flex;
align-items: center;
svg {
font-size: 1.5rem;
}
.text {
margin-left: 0.5rem;
flex: 1;
}
&.checked {
svg {
color: #ac22cf;
}
.text {
color: #b7adbd;
text-decoration: line-through;
}
}
}
.remove {
display: flex;
align-items: center;
font-size: 1.5rem;
color: #ff6b6b;
cursor: pointer;
&:hover {
color: #ff9b87;
}
}
// components in the same level
& + & {
border-top: 1px solid #e2dee6;
}
}
TodoList.js
import React from 'react';
import TodoListItem from './TodoListItem';
import '../styles/TodoList.scss'
const TodoList = ({todos, onRemove, onChecked}) => {
return (
<div className='TodoList'>
{todos.map(todo => (
<TodoListItem
todo={todo}
key={todo.id}
onRemove={onRemove}
onChecked={onChecked}
/>
))}
</div>
);
};
export default TodoList;
TodoList.scss
.TodoList {
min-height: 320px;
max-height: 513px;
overflow-y: auto;
}
// import logo from './logo.svg';
import { useCallback, useRef, useState } from 'react';
import './App.css';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
import TodoTemplate from './components/TodoTemplate';
function App() {
const [todos, setTodos] = useState([
{
id: 1,
text: 'react basic',
checked: true,
},
{
id: 2,
text: 'styling the components',
checked: true,
},
{
id: 3,
text: 'making a todo app',
checked: false,
},
])
const nextId = useRef(4);
const onInsert = useCallback(text => {
const todo = {
id: nextId.current,
text: text,
checked: false,
};
setTodos(todos.concat(todo));
nextId.current += 1;
}, [todos]);
const onRemove = useCallback(
id => {
setTodos(todos.filter(todo => todo.id !== id));
},
[todos]
);
const onChecked = useCallback(
id => {
setTodos(todos.map(todo => todo.id === id ? {...todo, checked: !todo.checked} : todo))
},
[todos]
);
return (
<TodoTemplate>
<TodoInsert onInsert={onInsert}/>
<TodoList todos={todos} onRemove={onRemove} onChecked={onChecked}/>
</TodoTemplate>
);
}
export default App;
.....
컴포넌트 최적화 한거 분명 작성했는데 왜 날라갔지.....
TodoListItem.js
export default React.memo(TodoListItem);
App.js
// import logo from './logo.svg';
import { useCallback, useRef, useState } from 'react';
import './App.css';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
import TodoTemplate from './components/TodoTemplate';
function createBultTodos() {
const array = [];
for(let i = 1; i <= 2500; i++) {
array.push({
id: i,
text: `task ${i}`,
checked: false,
});
}
return array;
}
function App() {
const [todos, setTodos] = useState(createBultTodos);
const nextId = useRef(2501);
const onInsert = useCallback(
(text) => {
const todo = {
id: nextId.current,
text: text,
checked: false,
};
setTodos((todos) => todos.concat(todo));
nextId.current += 1;
},
[],
);
const onRemove = useCallback(
(id) => {
setTodos((todos) => todos.filter((todo) => todo.id !== id));
},
[],
);
const onChecked = useCallback(
(id) => {
setTodos(
(todos) =>
todos.map((todo) =>
todo.id === id ? { ...todo, checked: !todo.checked } : todo,
),
);
},
[],
);
return (
<TodoTemplate>
<TodoInsert onInsert={onInsert} />
<TodoList todos={todos} onRemove={onRemove} onChecked={onChecked} />
</TodoTemplate>
);
}
export default App;
TodoList.js
export default React.memo(TodoList);
App.js
// import logo from './logo.svg';
import { useCallback, useReducer, useRef, useState } from 'react';
import './App.css';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
import TodoTemplate from './components/TodoTemplate';
function createBultTodos() {
const array = [];
for (let i = 1; i <= 2500; i++) {
array.push({
id: i,
text: `task ${i}`,
checked: false,
});
}
return array;
}
function todoReducer(todos, action) {
switch (action.type) {
case 'INSERT':
return todos.concat(action.todo);
case 'REMOVE':
return todos.filter((todo) => todo.id !== action.id);
case 'CHECKED':
return todos.map((todo) =>
todo.id === action.id ? { ...todo, checked: !todo.checked } : todo,
);
default:
return todos;
}
}
function App() {
// 초기상태를 undefined로 설정
// 세번째 파라미터에 초기상태 만드는 함수를 넣으면, 첫 렌더링에만 해당 함수 호출
const [todos, dispatch] = useReducer(todoReducer, undefined, createBultTodos);
const nextId = useRef(2501);
const onInsert = useCallback((text) => {
const todo = {
id: nextId.current,
text: text,
checked: false,
};
dispatch({ type: 'INSERT', todo });
nextId.current += 1;
}, []);
const onRemove = useCallback((id) => {
dispatch({ type: 'REMOVE', id });
}, []);
const onChecked = useCallback((id) => {
dispatch({ type: 'CHEKCED', id });
}, []);
return (
<TodoTemplate>
<TodoInsert onInsert={onInsert} />
<TodoList todos={todos} onRemove={onRemove} onChecked={onChecked} />
</TodoTemplate>
);
}
export default App;