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 [editMode, setEditMode] = useState(false);
const readMode = !editMode;
const enableEditMode = () => {
setEditMode(true);
};
const removeTodo = () => {
_removeTodo(todo.id);
};
const cancleEdit = () => {
setEditMode(false);
};
const commitEdit = () => {
setEditMode(false);
};
return (
<li className="flex items-center gap-x-3 mb-3">
<span className="badge badge-accent badge-outline">{todo.id}</span>
{readMode ? (
<>
<span>{todo.title}</span>
<button className="btn btn-outline btn-accent" onClick={enableEditMode}>
수정
</button>
<button className="btn btn-accent" onClick={removeTodo}>
삭제
</button>
</>
) : (
<>
<input
className="input input-bordered"
type="text"
placeholder="할 일 써"
value={todo.title}
/>
<button className="btn btn-accent" onClick={commitEdit}>
수정완료
</button>
<button className="btn btn-accent" onClick={cancleEdit}>
수정취소
</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} />
</>
);
};
이 코드는 JavaScript의 함수입니다. 이 함수는
modifyTodo라고 하며, 할 일 목록에서 특정 항목의 제목을 수정하는 기능을 합니다. 여기서todos는 할 일 목록을 나타내는 배열이고, 각 할 일 항목은 최소한id와title속성을 가지고 있다고 가정합니다.setTodos는todos배열을 업데이트하기 위한 함수로 보입니다. 아마도 React의 상태 업데이트 함수일 가능성이 높습니다.이 함수는 두 개의 매개변수를 받습니다:
id: 수정하려는 할 일 항목의 식별자입니다.title: 해당 할 일 항목에 새로 할당하려는 제목입니다.함수 내부에서는 다음과 같은 과정을 통해 작동합니다:
1.todos배열을map함수를 사용해 순회합니다.map함수는 배열의 각 요소에 대해 주어진 함수를 실행하고, 결과로 새 배열을 생성합니다.
2. 배열의 각 요소 (todo)에 대해, 현재 요소의id가 매개변수로 받은id와 같지 않은 경우 (todo.id != id), 현재 요소를 그대로 반환합니다. 즉, 해당 요소는 변경되지 않습니다.
3. 만약 현재 요소의id가 매개변수로 받은id와 같은 경우, 즉 수정하려는 항목이라면, 현재todo객체를 펼친 후 ({ ...todo }),title속성을 새로운title매개변수의 값으로 덮어쓰기합니다. 이렇게 하여 해당 항목만 제목이 수정된 새로운 객체를 생성합니다.
4. 이 과정을 거쳐 생성된 새 배열 (newTodos)는setTodos함수를 사용하여todos상태를 업데이트합니다.이 로직을 통해, 원본
todos배열에서 특정id를 가진 항목만 그 제목을 새로운 값으로 수정하고, 나머지 항목들은 그대로 유지합니다. React에서 이렇게 상태를 업데이트하면 해당 컴포넌트는 수정된todos배열을 반영하여 리렌더링됩니다.
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,
modifyTodo: _modifyTodo
}) => {
const [editMode, setEditMode] = useState(false);
const [newTodoTitle, setNewTodoTitle] = useState(todo.title);
const readMode = !editMode;
const enableEditMode = () => {
setEditMode(true);
};
const removeTodo = () => {
_removeTodo(todo.id);
};
const cancleEdit = () => {
setEditMode(false);
setNewTodoTitle(todo.title);
};
const commitEdit = () => {
if (newTodoTitle.trim().length == 0) return;
_modifyTodo(todo.id, newTodoTitle.trim());
setEditMode(false);
};
return (
<li className="flex items-center gap-x-3 mb-3">
<span className="badge badge-accent badge-outline">{todo.id}</span>
{readMode ? (
<>
<span>{todo.title}</span>
<button className="btn btn-outline btn-accent" onClick={enableEditMode}>
수정
</button>
<button className="btn btn-accent" onClick={removeTodo}>
삭제
</button>
</>
) : (
<>
<input
className="input input-bordered"
type="text"
placeholder="할 일 써"
value={newTodoTitle}
onChange={(e) => setNewTodoTitle(e.target.value)}
/>
<button className="btn btn-accent" onClick={commitEdit}>
수정완료
</button>
<button className="btn btn-accent" onClick={cancleEdit}>
수정취소
</button>
</>
)}
</li>
);
};
const TodoList = ({ todos, removeTodo, modifyTodo }) => {
return (
<>
{todos.length == 0 ? (
<h4>할 일 없음</h4>
) : (
<>
<h4>할 일 목록</h4>
<ul>
{todos.map((todo) => (
<TodoListItem
key={todo.id}
todo={todo}
removeTodo={removeTodo}
modifyTodo={modifyTodo}
/>
))}
</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);
};
const modifyTodo = (id, title) => {
const newTodos = todos.map((todo) =>
todo.id != id ? todo : { ...todo, title }
);
setTodos(newTodos);
};
return (
<>
<NewTodoForm addTodo={addTodo} />
<hr />
<TodoList todos={todos} removeTodo={removeTodo} modifyTodo={modifyTodo} />
</>
);
};
const NewTodoForm = ({ todoStatus }) => {
const [newTodoTitle, setNewTodoTitle] = useState("");
const addTodo = () => {
if (newTodoTitle.trim().length == 0) return;
const title = newTodoTitle.trim();
todoStatusaddTodo(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,
todoStatus
}) => {
const [editMode, setEditMode] = useState(false);
const [newTodoTitle, setNewTodoTitle] = useState(todo.title);
const readMode = !editMode;
const enableEditMode = () => {
setEditMode(true);
};
const removeTodo = () => {
todoStatus.removeTodo(todo.id);
};
const cancleEdit = () => {
setEditMode(false);
setNewTodoTitle(todo.title);
};
const commitEdit = () => {
if (newTodoTitle.trim().length == 0) return;
todoStatus.modifyTodo(todo.id, newTodoTitle.trim());
setEditMode(false);
};
return (
<li className="flex items-center gap-x-3 mb-3">
<span className="badge badge-accent badge-outline">{todo.id}</span>
{readMode ? (
<>
<span>{todo.title}</span>
<button className="btn btn-outline btn-accent" onClick={enableEditMode}>
수정
</button>
<button className="btn btn-accent" onClick={removeTodo}>
삭제
</button>
</>
) : (
<>
<input
className="input input-bordered"
type="text"
placeholder="할 일 써"
value={newTodoTitle}
onChange={(e) => setNewTodoTitle(e.target.value)}
/>
<button className="btn btn-accent" onClick={commitEdit}>
수정완료
</button>
<button className="btn btn-accent" onClick={cancleEdit}>
수정취소
</button>
</>
)}
</li>
);
};
const TodoList = ({ todoStatus }) => {
return (
<>
{todoStatus.todos.length == 0 ? (
<h4>할 일 없음</h4>
) : (
<>
<h4>할 일 목록</h4>
<ul>
{todoStatus.todos.map((todo) => (
<TodoListItem
key={todo.id}
todo={todo}
todoStatus={todoStatus}
/>
))}
</ul>
</>
)}
</>
);
};
const useTodoStatus = () => {
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);
};
const modifyTodo = (id, title) => {
const newTodos = todos.map((todo) =>
todo.id != id ? todo : { ...todo, title }
);
setTodos(newTodos);
};
return {
todos,
addTodo,
removeTodo,
modifyTodo
};
};
const App = () => {
const todoStatus = useTodoStatus(); // 리액트 커스텀 훅
return (
<>
<NewTodoForm todoStatus={todoStatus} />
<hr />
<TodoList todoStatus={todoStatus}/>
</>
);
};
import immer, { produce } from "https://cdn.skypack.dev/immer";const addTodo = () => {
const newTodos = projuct(todos. draft => {
draft.push({id:3, title:"제목3"});
});
};
const arr = [1,2,3];
const newArr = [...arr,4];
const App = () => {
const [todos, setTodos] = useState([
{ id: 1, title: "제목1" },
{ id: 2, title: "제목2" }
]);
const addTodo = () => {
// const newTodos = [...todos, {id:3, title:"제목3"}];
const newTodos = produce(todos, (draft) => {
draft.push({ id: 3, title: "제목3" });
});
setTodos(newTodos);
};
const modifyTodo = () => {
// const newTodos = todos.map((todo) => todo.id != 1 ? todo : {...todo,title:"zxcv"});
// 실무코드
setTodos(produce(todos, (draft) => {
draft[1].title = "zxcv";
}));
};
const removeTodo = () => {
// const newTodos = todos.filter((todo,index) => index != 1);
const newTodos = produce(todos, (draft) => {
draft.splice(1, 1);
});
setTodos(newTodos);
};
return (
<>
todos : {JSON.stringify(todos)}
<hr />
<button onClick={addTodo}>추가</button>
<button onClick={modifyTodo}>수정</button>
<button onClick={removeTodo}>삭제</button>
</>
);
};
const NewTodoForm = ({ todoStatus }) => {
const [newTodoTitle, setNewTodoTitle] = useState("");
const addTodo = () => {
if (newTodoTitle.trim().length == 0) return;
const title = newTodoTitle.trim();
todoStatusaddTodo(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,
todoStatus
}) => {
const [editMode, setEditMode] = useState(false);
const [newTodoTitle, setNewTodoTitle] = useState(todo.title);
const readMode = !editMode;
const enableEditMode = () => {
setEditMode(true);
};
const removeTodo = () => {
todoStatus.removeTodo(todo.id);
};
const cancleEdit = () => {
setEditMode(false);
setNewTodoTitle(todo.title);
};
const commitEdit = () => {
if (newTodoTitle.trim().length == 0) return;
todoStatus.modifyTodo(todo.id, newTodoTitle.trim());
setEditMode(false);
};
return (
<li className="flex items-center gap-x-3 mb-3">
<span className="badge badge-accent badge-outline">{todo.id}</span>
{readMode ? (
<>
<span>{todo.title}</span>
<button className="btn btn-outline btn-accent" onClick={enableEditMode}>
수정
</button>
<button className="btn btn-accent" onClick={removeTodo}>
삭제
</button>
</>
) : (
<>
<input
className="input input-bordered"
type="text"
placeholder="할 일 써"
value={newTodoTitle}
onChange={(e) => setNewTodoTitle(e.target.value)}
/>
<button className="btn btn-accent" onClick={commitEdit}>
수정완료
</button>
<button className="btn btn-accent" onClick={cancleEdit}>
수정취소
</button>
</>
)}
</li>
);
};
const TodoList = ({ todoStatus }) => {
return (
<>
{todoStatus.todos.length == 0 ? (
<h4>할 일 없음</h4>
) : (
<>
<h4>할 일 목록</h4>
<ul>
{todoStatus.todos.map((todo) => (
<TodoListItem
key={todo.id}
todo={todo}
todoStatus={todoStatus}
/>
))}
</ul>
</>
)}
</>
);
};
const useTodoStatus = () => {
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);
};
const modifyTodo = (id, title) => {
const newTodos = todos.map((todo) =>
todo.id != id ? todo : { ...todo, title }
);
setTodos(newTodos);
};
return {
todos,
addTodo,
removeTodo,
modifyTodo
};
};
const App = () => {
const todoStatus = useTodoStatus(); // 리액트 커스텀 훅
return (
<>
<NewTodoForm todoStatus={todoStatus} />
<hr />
<TodoList todoStatus={todoStatus}/>
</>
);
};
const App = () => {
const formInputNoRef = useRef(null);
const [number, setNumber] = useState("");
const [recordNumbers, setRecordNumbers] = useState([10, 20, 30]);
console.log(`AppCallCount : ${AppCallCount}`);
AppCallCount++;
const saveNumber = () => {
if (number === "") {
alert("숫자 입력해");
return;
}
setRecordNumbers([...recordNumbers, number]);
setNumber("");
formInputNoRef.current.focus();
};
const li = recordNumbers.map((el, index) => (
<li key={index}>
{index + 1}번 : {el}
</li>
));
return (
<>
<form
onSubmit={(e) => {
e.preventDefault();
saveNumber();
}}
>
<input
ref={formInputNoRef}
className="input input-bordered"
type="number"
placeholder="숫자 입력"
value={number}
onChange={(e) => setNumber(e.target.valueAsNumber)}
/>
<button className="btn btn-primary">기록</button>
</form>
<hr />
<h3>기록 된 숫자 v1</h3>
{JSON.stringify(recordNumbers)}
<hr />
<h3>기록 된 숫자 v2</h3>
<ul>{li}</ul>
<hr />
<h3>기록 된 숫자 v2-2</h3>
<ul>
{recordNumbers.map((el, index) => (
<li key={index}>
{index + 1}번 : {el}
</li>
))}
</ul>
</>
);
};
[] 의존성 배열, 배열안에 어떤 값이 들어있으면 그 값이 바뀌었을때 함수가 실행이 된다.
- 빈 배열이 없으면 useEffect 안에 있는 로직은 리렌더링 된다.
- 배열이 있으면 useEffect 안에 있는 로직은 한번만 실행된다.
import React, { useState, useEffect } from "https://cdn.skypack.dev/react@18";
import ReactDOM from "https://cdn.skypack.dev/react-dom@18";
let AppCallCount = 0;
const App = () => {
useEffect(() => {
AppCallCount++;
console.log(`App이 ${AppCallCount}번 실행`);
}, []); // [] : 의존성 배열
const [no,setNo] = useState(0);
return (
<>
<button onClick={() => setNo(no + 1)} >증가 : {no}</button>
</>
);
};
import React, {
useState,
useRef,
useEffect
} from "https://cdn.skypack.dev/react@18";
import ReactDOM from "https://cdn.skypack.dev/react-dom@18";
let AppCallCount = 0;
const App = () => {
AppCallCount++;
console.log(`1App이 ${AppCallCount}번 실행`);
const inputNameRef = useRef(null);
const inputAgeRef = useRef(null);
const [no, setNo] = useState(0);
// setTimeout(() => inputNameRef.current.focus());
useEffect(()=>{
inputNameRef.current.focus();
},[]);
return (
<>
<input ref={inputNameRef} type="text" placeholder="이름" />
<hr />
<input ref={inputAgeRef} type="number" placeholder="나이" />
<hr />
<button
onClick={() => {
setNo(no + 1);
inputAgeRef.current.focus();
}}
>
증가 : {no}
</button>
</>
);
};
import React, { useState } from "https://cdn.skypack.dev/react@18";
import ReactDOM from "https://cdn.skypack.dev/react-dom@18";
let AppCallCount = 0;
let SubCallCount = 0;
const Sub = () => {
SubCallCount++;
console.log(`Sub ${SubCallCount}번 실행됨`);
const [no, setNo] = useState(0);
return (
<>
<button onClick={() => setNo(no + 1)}>Sub 버튼 : {no}</button>
</>
);
};
const App = () => {
AppCallCount++;
console.log(`App ${AppCallCount}번 실행됨`);
const [no, setNo] = useState(0);
return (
<>
<div style={{ border: "5px solid red", padding: 10 }}>
<Sub />
<hr />
<button onClick={() => setNo(no + 1)}>App 버튼 : {no}</button>
</div>
</>
);
};