📅2024. 03. 27 73일차
//생략
const TodoListItem = ({ todo, index, removeTodo, updateTodo }) => {
const [isEditing, setIsEditing] = useState(false);
const [editedTodo, setEditedTodo] = useState(todo);
const handleEditChange = (e) => {
setEditedTodo(e.target.value);
};
const handleEditSubmit = () => {
if (editedTodo.trim().length === 0) return;
updateTodo(index, editedTodo);
setIsEditing(false);
};
const handleEditClick = () => {
setIsEditing(true);
};
const handleRemoveClick = () => {
removeTodo(index);
};
return (
<>
<li className="flex items-center gap-x-3">
{isEditing ? (
<>
<input
type="text"
value={editedTodo}
onChange={handleEditChange}
/>
<button className="btn btn-secondary" onClick={handleEditSubmit}>
완료
</button>
</>
) : (
<>
<span>
{index + 1}번 째 할일 : {todo}
</span>
<button className="btn btn-secondary" onClick={handleEditClick}>
수정
</button>
</>
)}
<button className="btn btn-secondary" onClick={handleRemoveClick}>
삭제
</button>
</li>
</>
);
};
const TodoList = ({ todos, removeTodo, updateTodo }) => {
return (
<div>
{todos.length === 0 ? (
<h5>할 일이 없습니다.</h5>
) : (
<>
<h5>새 할일</h5>
<nav>
<ul>
{todos.map((todo, index) => (
<TodoListItem
key={index}
todo={todo}
index={index}
removeTodo={removeTodo}
updateTodo={updateTodo}
/>
))}
</ul>
</nav>
</>
)}
</div>
);
};
const App = () => {
const [todos, setTodos] = useState([]);
const addTodo = (newTitle) => {
if (newTitle.trim().length === 0) return;
setTodos([...todos, newTitle.trim()]);
};
const updateTodo = (index, updatedTodo) => {
const newTodos = todos.map((todo, todoIndex) =>
todoIndex === index ? updatedTodo : todo
);
setTodos(newTodos);
};
const removeTodo = (index) => {
const newTodos = todos.filter((_, _index) => _index !== index);
setTodos(newTodos);
};
return (
<>
<TodoWriteForm addTodo={addTodo} />
<hr />
<TodoList todos={todos} removeTodo={removeTodo} updateTodo={updateTodo} />
</>
);
};
console.clear();
import React, { useState } from "https://cdn.skypack.dev/react@18";
import ReactDOM from "https://cdn.skypack.dev/react-dom@18";
const NewTodoForm = ({addTodo:_addTodo}) => {
const [newTodoTitle, setNewTodoTitle] = useState("");
const addTodo = () => {
if(newTodoTitle.trim().length == 0) return;
const id = 1;
const title = newTodoTitle;
const newTodo = {
id,
title
};
_addTodo(newTodo);
setNewTodoTitle('');
}
return (
<>
<div className="flex items-center gap-x-3">
<input
className="input input-bordered"
type="text"
placeholder="새 할일 입력해"
value={newTodoTitle}
onChange={(e) => setNewTodoTitle(e.target.value)}
/>
<button className="btn btn-primary" onClick={addTodo}>할 일 추가</button>
</div>
</>
);
};
const TodoList = (todos) => {
return <>
{JSON.stringify(todos)}
</>
}
const App = () => {
const [todos, setTodos] = useState([]);
const addTodo = (newTodo) => {
setTodos([...todos, newTodo]);
};
return (
<>
<NewTodoForm addTodo={addTodo} />
<hr/>
<TodoList todos={todos} />
</>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
const NewTodoForm = ({ addTodo: _addTodo, todos }) => {
const [newTodoTitle, setNewTodoTitle] = useState("");
const addTodo = () => {
if (newTodoTitle.trim().length == 0) return;
const id = todos.length + 1;
const title = newTodoTitle;
const newTodo = {
id,
title
};
_addTodo(newTodo, todos);
setNewTodoTitle("");
};
return (
<>
<div className="flex items-center gap-x-3">
<input
className="input input-bordered"
type="text"
placeholder="새 할일 입력해"
value={newTodoTitle}
onChange={(e) => setNewTodoTitle(e.target.value)}
/>
<button className="btn btn-primary" onClick={addTodo}>
할 일 추가
</button>
</div>
</>
);
};
const TodoList = ({ todos }) => {
return <>{JSON.stringify(todos)}</>;
};
const App = () => {
const [todos, setTodos] = useState([]);
const addTodo = (newTodo) => {
setTodos([...todos, newTodo]);
};
return (
<>
<NewTodoForm addTodo={addTodo} todos={todos}/>
<hr />
<TodoList todos={todos} />
</>
);
};
슨생님.var
console.clear();
import React, { useState } from "https://cdn.skypack.dev/react@18";
import ReactDOM from "https://cdn.skypack.dev/react-dom@18";
const NewTodoForm = ({addTodo:_addTodo}) => {
const [newTodoTitle, setNewTodoTitle] = useState("");
const addTodo = () => {
if(newTodoTitle.trim().length == 0) return;
const title = newTodoTitle;
const newTodo = {
id : null,
title
};
_addTodo(newTodo);
setNewTodoTitle('');
}
return (
<>
<div className="flex items-center gap-x-3">
<input
className="input input-bordered"
type="text"
placeholder="새 할일 입력해"
value={newTodoTitle}
onChange={(e) => setNewTodoTitle(e.target.value)}
/>
<button className="btn btn-primary" onClick={addTodo}>할 일 추가</button>
</div>
</>
);
};
const TodoList = (todos) => {
return <>
{JSON.stringify(todos)}
</>
}
const App = () => {
const [todos, setTodos] = useState([]);
const [lastTodoId, setLastTodoId] = useState(0);
const addTodo = (newTodo) => {
newTodo.id = lastTodoId + 1;
setTodos([...todos, newTodo]);
setLastTodoId(newTodo.id);
};
return (
<>
<NewTodoForm addTodo={addTodo} />
<hr/>
<TodoList todos={todos} />
</>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
console.clear();
import React, { useState } from "https://cdn.skypack.dev/react@18";
import ReactDOM from "https://cdn.skypack.dev/react-dom@18";
const NewTodoForm = ({ addTodo: _addTodo }) => {
const [newTodoTitle, setNewTodoTitle] = useState("");
const addTodo = () => {
if (newTodoTitle.trim().length == 0) return;
const title = newTodoTitle.trim();
_addTodo(title);
setNewTodoTitle("");
};
return (
<>
<div className="flex items-center gap-x-3">
<input
className="input input-bordered"
type="text"
placeholder="새 할일 입력해"
value={newTodoTitle}
onChange={(e) => setNewTodoTitle(e.target.value)}
/>
<button className="btn btn-primary" onClick={addTodo}>
할 일 추가
</button>
</div>
</>
);
};
const TodoListItem = ({ todo, removeTodo: _removeTodo }) => {
const removeTodo = () => {
_removeTodo(todo.id);
};
return (
<li className="flex items-center gap-x-3 mb-3">
<span className="badge badge-primary badge-outline">{todo.id}</span>
<span>{todo.title}</span>
<button className="btn btn-primary" onClick={removeTodo}>
삭제
</button>
</li>
);
};
const TodoList = ({ todos, removeTodo }) => {
return (
<>
{todos.length == 0 ? (
<h4>할 일 없음</h4>
) : (
<>
<h4>할 일 목록</h4>
<ul>
{todos.map((todo) => (
<TodoListItem key={todo.id} todo={todo} removeTodo={removeTodo} />
))}
</ul>
</>
)}
</>
);
};
const App = () => {
const [todos, setTodos] = useState([]);
const [lastTodoId, setLastTodoId] = useState(0);
const addTodo = (title) => {
const id = lastTodoId + 1;
const newTodo = {
id,
title
};
setTodos([...todos, newTodo]);
setLastTodoId(id);
};
const removeTodo = (id) => {
const newTodos = todos.filter(todo => todo.id != id);
setTodos(newTodos);
};
return (
<>
<NewTodoForm addTodo={addTodo} />
<hr />
<TodoList todos={todos} removeTodo={removeTodo} />
</>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
const [state, setState] = useState(초기값)
state는 위와 같이 선언한다. state는 변수, setState는 state를 변경시키는 함수, useState는 state, setState를 return 하면서 초기값을 설정해주는 hook이다.
setState()는 컴포넌트의 state 객체에 대한 업데이트를 실행한다. state가 변경되면, 컴포넌트는 리렌더링된다.
setState()는 리액트의 함수형 컴포넌트 내에서 상태를 관리하기 위해 사용하는 hooks인 useState()를 통해 반환되는 함수이다.
기본적으로 setState()함수는 state를 변경하고 state가 포함된 해당 컴포넌트를 다시 렌더링한다. 그런데 이게 동기적으로 동작하는 것이 아니라 비동기적으로 동작함에 주목해야한다(= setState()를 실행하자마자 바로 state를 변경하고 바로 렌더링하는게 아니다!).
1) setNumber(number + 1);
2) setNumber((_number) => _number + 1);
1번 방법은 직접적으로 2번 방법은 함수형 업데이트 방법
const Num1Button = ({ num, setNum }) => {
const onClick = () => {
setNum(num + 1);
setNum(num + 1);
};
return <button onClick={onClick}>증가</button>;
};
const Num2Button = ({ setNum }) => {
const onClick = () => {
setNum((num) => num + 1);
setNum((num) => num + 1);
};
return <button onClick={onClick}>증가</button>;
};
const App = () => {
const [num1, setNum1] = useState(0);
const [num2, setNum2] = useState(0);
return (
<>
num1 : {num1} <Num1Button num={num1} setNum={setNum1} />
<hr />
num2 : {num2} <Num2Button setNum={setNum2} />
</>
);
};
다른 이유는 React의 useState hook은 비동기적으로 상태를 업데이트한다. 이것은 함수형 업데이트를 사용하는 것과 직접 값을 전달하는 것 사이의 차이 때문에 발생한다.
Num1Button에서 setNum(num + 1)을 두 번 호출하면, 이 코드는 현재 num 값에 1을 추가한 값을 상태로 설정한다. 이 작업은 비동기적으로 이루어지므로, 두 번의 호출 중 먼저 호출된 setNum(num + 1)에서는 이전 상태의 num 값을 기반으로 한다. 따라서 두 번 호출해도 결과적으로는 1이 증가한 값만 적용된다.
Num2Button에서는 함수형 업데이트를 사용하여 setNum을 호출한다. 함수형 업데이트를 사용하면 React는 이전 상태를 보장하고 업데이트를 수행한다. 따라서 두 번의 setNum((num) => num + 1) 호출은 두 번의 증가가 반영된다.