App.js
import './App.css';
import { useState, useRef } from 'react';
import CreateTodo from './components/CreateTodo';
import TodoList from './components/TodoList';
function App() {
const [ inputs, setInputs ] = useState({
todo:"",
});
const onChange = (e) => {
const { name, value } = e.target;
setInputs({
...inputs,
[name]:value
})
console.log(inputs);
}
const { todo } = inputs;
const [ todos, setTodos] = useState([
])
const nextId = useRef(1);
const onCreate= () => {
const todolist = {
id:nextId.current,
todo,
}
setTodos([...todos, todolist]);
setInputs({
todo:"",
})
nextId.current +=1;
}
const onDelete = (id) => {
setTodos(todos.filter(todolist=> id !== todolist.id));
}
const onToggle = (id) => {
setTodos(todos.map(todolist=> id === todolist.id ? {...todolist, active: !todolist.active} : todolist ));
}
return (
<div className="App">
<CreateTodo todo={todo} onChange={onChange} onCreate={onCreate} />
<TodoList todos={todos} onDelete={onDelete} onToggle={onToggle}/>
</div>
);
}
export default App;
CreateTodo.js
import React from 'react';
import './TodoList.css'
const CreateTodo= ({todo, onChange, onCreate}) => {
return (
<div>
<header>
<h1>to do list</h1>
<div id='todo_box'>
<input name="todo" value = {todo} onChange={onChange}/>
<button onClick={onCreate}>+</button>
</div>
</header>
</div>
);
};
export default CreateTodo;
TodoList.js
import React from 'react';
import './TodoList.css'
const Todo = ({todo, onDelete, onToggle}) => {
return (
<div id= "todo">
<span id="todotodo" className={todo.active ? 'active': ""} onClick={()=>{onToggle(todo.id) }}>{todo.todo}</span>
<button id="delBtn" onClick={()=>{
onDelete(todo.id);
}}>X</button>
</div>
)
}
const TodoList = ({todos, onDelete, onToggle}) => {
return (
<div class="lists">
{todos.map(todo =><Todo todo={todo} key={todo.id} onDelete={onDelete} onToggle={onToggle}/> )}
</div>
);
};
export default TodoList;
TodoList.css
*{padding: 0; margin: 0 auto; box-sizing: border-box;}
header {
background-image: linear-gradient(135deg, #d2f985 0%, #fdd2c8 100%);
width: 900px;
height: 140px;
}
h1 {
padding-top: 25px;
padding-bottom: 8px;
color: #222;
}
#todo_box {
display: flex;
align-items: center;
}
input {
width: 600px;
height: 28px;
padding-left: 10px;
border-radius: 25px;
outline: none;
border: none;
margin-right: -120px;
}
.lists {
width: 900px;
background-color: #eee;
padding: 8px 0;
padding-left: 90px;
text-align: left;
line-height: 2.5;
}
button {
padding: 0px 8px;
border-radius: 25px;
border: none;
outline: none;
background-color: #fff;
font-size: 24px;
cursor: pointer;
}
#todo {
display: flex;
}
#todotodo {
margin-left: 0px;
cursor: pointer;
}
#delBtn {
font-size: 16px;
background-color: transparent;
margin-right: 80px;
}
.active {
text-decoration: line-through;
}
쌤 코드
App.js
import './App.css';
import { useState, useRef } from 'react' // id를 계속 증가시키기위해 useRef 사용
import TodoLists from './components/TodoLists';
import CreateTodo from './components/CreateTodo';
function App() {
const name = "abc";
const [ list, setList ] = useState("");
// input의 값을 입력할 때 (input의 value가 변경될 때)
// onChange 함수를 실행
// state인 list값을 input의 value 값으로 업데이트
const onChange = (e)=> {
const { value } = e.target;
// console.log(e.target)
setList(value);
}
// CreateTodo 컴포넌트에서 + 버튼을 클릭하면
// todos배열에 할 일 객체가 추가됨
const onCreate = () => {
const listobj = {
id: nextId.current,
list: list,
isDone: false,
}
setTodos([...todos, listobj]); // 원래 배열(todos)을 펼쳐서 새로운 애(listobj)를 추가하기
nextId.current +=1;
setList("");
}
const [ todos, setTodos ] = useState([
{
id:1,
list: "해야할일1",
isDone: false,
},
{
id:2,
list: "해야할일2",
isDone: false,
},
{
id:3,
list: "해야할일3",
isDone: false,
}
])
const nextId = useRef(todos.length+1);
// 항목삭제
// 삭제 클릭시 id값을 인수로 받아서
// todos배열에서 id값이 다른 객체만 업데이트
const onDelete = (id)=> {
setTodos(todos.filter(todo=>id !== todo.id));
}
return (
<div className="App">
<CreateTodo list={list} onChange={onChange} onCreate={onCreate}/>
<TodoLists todos={todos} name={name} onDelete={onDelete}/>
</div>
);
}
export default App;
CreateTodo.js
import React from 'react';
import './TodoList.css'
const CreateTodo = ({onChange, list, onCreate}) => {
return (
<div>
<header>
<h2>to do list</h2>
<div id='todo_box'>
<input type="text" value={list} name="newlist" onChange={onChange}/>
{/* (e)=>onChange(e.target.value) */}
<button onClick={onCreate}>+</button>
</div>
</header>
</div>
);
};
export default CreateTodo;
TodoList.js
import React from 'react';
//rsc
const TodoList = ({todo, onDelete}) => {
return (
<div id="todo">
<span id="todotodo">{todo.list}</span>
<button id="delBtn" onClick={()=>{onDelete(todo.id)}}>X</button>
</div>
);
};
export default TodoList;
TodoLists.js
import React from 'react';
import TodoList from './TodoList';
import './TodoList.css'
const TodoLists = ({todos, onDelete}) => {
return (
<div class="lists">
{todos.map(todo=><TodoList todo={todo} key={todo.id} onDelete={onDelete}/>)}
</div>
);
};
export default TodoLists;
Reducer 버전
AppReducer.js
import './App.css';
import { useRef, useReducer } from 'react';
import CreateTodo from './components/CreateTodo';
import TodoLists from './components/TodoLists';
const initialState = {
text: "",
todos: [
{
id:1,
todotext: "리액트 공부하기",
isDone: false,
},
{
id:2,
todotext: "타입스크립트 공부하기",
isDone: false,
},
{
id:3,
todotext: "reducer 공부하기",
isDone: false,
}
]
}
function reducer(state, action) {
switch(action.type) {
case "CHANGE_INPUT":
return {
...state,
text :action.text
};
case "CREATE_TODO":
return {
text: "",
todos: state.todos.concat(action.todo)
}
case "DELETE_TODO":
return {
...state,
todos: state.todos.filter(todo=> todo.id !== action.id)
};
case "ISDONE_TODO":
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.id ? {...todo, isDone: !todo.isDone}: todo)
}
default:
return state;
}
}
function App() {
const [state, dispatch] = useReducer(reducer, initialState)
const { text, todos } = state;
const onChange = (e) => {
dispatch({
type: "CHANGE_INPUT",
text: e.target.value
})
console.log(e.target.value)
}
const nextId = useRef(4);
const onCreate = () => {
dispatch({
type: "CREATE_TODO",
todo: {
id: nextId.current,
todotext: text,
isDone: false,
}
})
}
const onDelete = (id) => {
dispatch({
type: "DELETE_TODO",
id: id,
})
}
const onToggle = (id) => {
dispatch({
type: "ISDONE_TODO",
id: id
})
}
return (
<div className="App">
<CreateTodo text = {text} onChange={onChange} onCreate={onCreate}/>
<TodoLists todos={todos} onDelete={onDelete} onToggle={onToggle}/>
</div>
);
}
export default App;
CreateTodo.js
import './App.css';
import { useRef, useReducer } from 'react';
import CreateTodo from './components/CreateTodo';
import TodoLists from './components/TodoLists';
const initialState = {
text: "",
todos: [
{
id:1,
todotext: "리액트 공부하기",
isDone: false,
},
{
id:2,
todotext: "타입스크립트 공부하기",
isDone: false,
},
{
id:3,
todotext: "reducer 공부하기",
isDone: false,
}
]
}
function reducer(state, action) {
switch(action.type) {
case "CHANGE_INPUT":
return {
...state,
text :action.text
};
case "CREATE_TODO":
return {
text: "",
todos: state.todos.concat(action.todo)
}
case "DELETE_TODO":
return {
...state,
todos: state.todos.filter(todo=> todo.id !== action.id)
};
case "ISDONE_TODO":
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.id ? {...todo, isDone: !todo.isDone}: todo)
}
default:
return state;
}
}
function App() {
const [state, dispatch] = useReducer(reducer, initialState)
const { text, todos } = state;
const onChange = (e) => {
dispatch({
type: "CHANGE_INPUT",
text: e.target.value
})
console.log(e.target.value)
}
const nextId = useRef(4);
const onCreate = () => {
dispatch({
type: "CREATE_TODO",
todo: {
id: nextId.current,
todotext: text,
isDone: false,
}
})
}
const onDelete = (id) => {
dispatch({
type: "DELETE_TODO",
id: id,
})
}
const onToggle = (id) => {
dispatch({
type: "ISDONE_TODO",
id: id
})
}
return (
<div className="App">
<CreateTodo text = {text} onChange={onChange} onCreate={onCreate}/>
<TodoLists todos={todos} onDelete={onDelete} onToggle={onToggle}/>
</div>
);
}
export default App;
TodoList.js
import React from 'react';
//rsc
const TodoList = ({todo, onDelete, onToggle}) => {
return (
<div id="todo">
<span id="todotodo" className={todo.isDone ? 'isDone' : ""}
onClick ={()=> onToggle(todo.id)}>
{todo.todotext}
</span>
<button id="delBtn" onClick={()=>{onDelete(todo.id)}}>X</button>
</div>
// className={todo.isDone && 'isDone'}
);
};
export default TodoList;
TodoLists.js
import React from 'react';
import TodoList from './TodoList';
import './TodoList.css'
const TodoLists = ({todos, onDelete, onToggle}) => {
return (
<div className='lists'>
{todos.map(todo=><TodoList onToggle={onToggle} todo={todo} key={todo.id} onDelete={onDelete}/>)}
</div>
);
};
export default TodoLists;
TodoList.css
*{padding: 0; margin: 0 auto; box-sizing: border-box;}
header {
background-image: linear-gradient(135deg, #d2f985 0%, #fdd2c8 100%);
width: 900px;
height: 140px;
}
h2 {
padding-top: 25px;
padding-bottom: 8px;
color: #222;
}
#todo_box {
display: flex;
align-items: center;
}
input {
width: 600px;
height: 28px;
padding-left: 10px;
border-radius: 25px;
outline: none;
border: none;
margin-right: -120px;
}
.lists {
width: 900px;
background-color: #eee;
padding: 8px 0;
padding-left: 90px;
text-align: left;
line-height: 2.5;
}
button {
padding: 0px 8px;
border-radius: 25px;
border: none;
outline: none;
background-color: #fff;
font-size: 24px;
cursor: pointer;
}
#todo {
display: flex;
}
#todotodo {
margin-left: 0px;
cursor: pointer;
}
#delBtn {
font-size: 16px;
background-color: transparent;
margin-right: 80px;
}
.active {
text-decoration: line-through;
}
.isDone {
background-color: lightgoldenrodyellow;
}
'Stack > React' 카테고리의 다른 글
[React] 상태관리 (useState / useReducer) (0) | 2022.06.24 |
---|---|
[React] React Component Styling (0) | 2022.06.23 |
[React] 라이프 사이클 메서드(Life Cycle Method) (0) | 2022.06.21 |
[React] event-react(Event / ref) (0) | 2022.06.21 |
[React] 컴포넌트 (0) | 2022.06.20 |