✔️ 해당 글은 John Ahn님의 인프런강좌 "따라하며 배우는 리액트 A-Z"를 들으며 작성합니다.
이번에는 할 일 아이템을 수정, 저장하는 기능을 생성하겠습니다.
각 할 일 아이템의 삭제 버튼 옆에 EDIT버튼을 만들고,
그 버튼을 클릭하면 할 일 아이템의 ui를 바꿔서 글 수정이 가능하게하고,
EDIT버튼이 SAVE버튼이 되어 수정한 글을 저장할 수 있도록 만들고자합니다.
Item컴포넌트로 가서 State 두 개를 만듭니다.
하나는 글을 수정하는 State, 다른 하나는 수정이 완료된 글 State입니다.
const [IsEditing, setIsEditing] = useState(false);
const [editedTitle, setEditedTitle] = useState(title);
그리고 버튼ui도 만듭니다.
const BtnEdit = styled.button`
color: #245572;
background-color: transparent;
border: none;
padding: 6px 9px;
box-sizing: border-box;
cursor: pointer;
`;
...
<BtnEdit>EDIT</BtnEdit>
setState를 통해 EDIT 버튼을 클릭하면 해당 할 일 아이템의 ui가 바뀌는 함수를 만듭니다.
isEditing 은 기본적으로 false인데, 버튼을 클릭하면 setIsEditing을 true로 상태를 변경해줍니다.
<BtnEdit onClick={() => setIsEditing(true)}>EDIT</BtnEdit>
그리고 if문을 사용하여,
만약 isEditing이 true라면 "editing..."을 반환하고,
false라면 원래의 할 일 아이템 목록을 반환하도록 만들고자 했더니
map()이 걸립니다(...)
기존 Item 컴포넌트에 만들어둔 map()을 상위 컴포넌트인 List로 옮깁니다.
const List = React.memo(({ todoData, setTodoData, handleClick }) => {
return (
<WrapList>
{todoData.map((item) => (
<Item
todoData={todoData}
setTodoData={setTodoData}
handleClick={handleClick}
id={item.id}
title={item.title}
completed={item.completed}
/>
))}
</WrapList>
);
});
export default List;
상위 컴포넌트인 List에서 보내지는 id, title, completed props들도 Item에 넣어줍니다.
const Item = React.memo(
({ todoData, setTodoData, handleClick, id, title, completed }) => {
const getStyled = (completed) => {
return {
textDecoration: completed ? "line-through" : "none",
color: completed ? "#89acbe" : "#0c1214",
};
};
const handleCompletedChange = (id) => {
let newTodoData = todoData.map((item) => {
if (item.id === id) {
item.completed = !item.completed;
}
return item;
});
setTodoData(newTodoData);
};
const [isEditing, setIsEditing] = useState(false);
const [editied, setEditied] = useState(title);
return (
<WrapItem key={id}>
<ItemLabel style={getStyled(completed)} key={id}>
<Checkbox
type="checkbox"
defaultChecked={false}
onChange={() => handleCompletedChange(id)}
/>
{title}
</ItemLabel>
<BtnEdit onClick={() => setIsEditing(true)}>EDIT</BtnEdit>
<BtnDelete onClick={() => handleClick(id)}>X</BtnDelete>
</WrapItem>
);
}
}
);
export default Item;
이 상태에서 if 문을 만들어봅니다.
if (isEditing) {
return <div>editing...</div>;
} else {
return (
<WrapItem key={id}>
<ItemLabel style={getStyled(completed)} key={id}>
<Checkbox
type="checkbox"
defaultChecked={false}
onChange={() => handleCompletedChange(id)}
/>
{title}
</ItemLabel>
<BtnEdit onClick={() => setIsEditing(true)}>EDIT</BtnEdit>
<BtnDelete onClick={() => handleClick(id)}>X</BtnDelete>
</WrapItem>
);
}
네 다행히 EDIT버튼을 클릭한 해당 할 일 아이템의 모습만 바뀝니다.
✔️ 오늘의 교훈 : map() 돌릴 땐 상위 컴포넌트에서 돌리자(...)
Add 컴포넌트와 마찬가지로 submit되는 input을 위해 form으로 감싸주는 구조를 만듭니다.
이전에 작성했던 코드를 재사용하되, ItemLabel 상위에 form으로 감싸주고,
Checkbox의 type은 text로 변경 및 value와 타이핑 이벤트에 따라 변경될 것을 적용시켜줄 onChange함수도 적어줍니다.
if (isEditing) {
return (
<WrapItem key={id}>
<form>
<ItemLabel>
<Checkbox
type="text"
value={editedTitle}
onChange={hadleEditChange}
/>
</ItemLabel>
</form>
<BtnEdit type="submit">SAVE</BtnEdit>
<BtnDelete onClick={() => setIsEditing(false)}>X</BtnDelete>
</WrapItem>
);
} else ...
Add 컴포넌트의 input과 비슷합니다.
// 할 일 아이템 추가
const [value, setValue] = useState("");
const handleChange = (e) => {
setValue(e.target.value);
};
앞서 만든 useState를 사용합니다.
const [isEditing, setIsEditing] = useState(false);
...
const hadleEditChange = (e) => {
setEditedTitle(e.target.value);
};
수정이 됩니다.
form 태그에 적용할 onSubmit 함수를 만듭니다.
이 때 저장을 위해 save 버튼을 클릭해야 onSubmit이 일어나므로
save버튼에도 onClick 함수를 만들되, 그 이름이 onSubmit함수와 동일합니다.
이 경우는 form 안에 버튼이 함께 있지 않아서 각 태그별로 따로 지정해주면서 동시에 동일한 함수를 공유합니다.
<WrapItem key={id}>
<form onSubmit={handleEditSubmit}>
<ItemLabel>
<Checkbox
type="text"
value={editedTitle}
onChange={hadleEditChange}
/>
</ItemLabel>
</form>
<BtnEdit type="submit" onClick={handleEditSubmit}>
SAVE
</BtnEdit>
<BtnDelete onClick={() => setIsEditing(false)}>X</BtnDelete>
</WrapItem>
handleEditSubmit 함수를 작성합니다.
newTodoData를 선언하고, todoData를 map() 돌리는데,
이벤트가 일어나는(타이핑하는) item.id 와 id가 동일하다면
item.title은 앞서 수정했던 input(Checkbox type="text")의 value인 editedTitle이 되도록 하고 리턴합니다.
setTodoData를 통해 newTodoData(item.title이 수정된 todoData로 저장)로 상태 변경해주고,
수정하는 input의 화면을 전환하기 위해 setIsEditing은 false로 바꿔줍니다.
const handleEditSubmit = (e) => {
e.preventDefault();
let newTodoData = todoData.map((item) => {
if (item.id === id) {
item.title = editedTitle;
}
return item;
});
setTodoData(newTodoData);
setIsEditing(false);
};
수정사항이 잘 저장됩니다.
자잘한 스타일 수정도 합니당!
🧐 수정하는 부분이 많이 어렵네요 ㅠㅠ
아직 어떻게 풀어나가야 할 지 스스로 생각하는 힘이 부족한 것 같습니당..
TypeScript 타입스크립트 (0) | 2022.09.18 |
---|---|
Todo List 만들기로 배우는 React(feat.Styled component in emotion) - 8 (0) | 2022.09.07 |
Todo List 만들기로 배우는 React(feat.Styled component in emotion) - 6 (0) | 2022.09.07 |
Todo List 만들기로 배우는 React(feat.Styled component in emotion) - 5 (0) | 2022.09.07 |
Todo List 만들기로 배우는 React(feat.Styled component in emotion) - 4 (0) | 2022.09.06 |