// TodoItem.js
import itemStyle from '../Css/TodoItem.module.css';
import PropTypes from 'prop-types';
function TodoItem({ todoItem, todoList, setTodoList }){
function removeItem(e){
const parentLi = e.target.parentElement; // (1)
parentLi.remove(); // (1)-1
}
return(
<>
<li className={itemStyle.item}>
<input type='checkbox' />
<span>{ todoItem }</span>
<button type='button' className={itemStyle.editButton}>🔺</button>
<button
type='button'
className={itemStyle.deleteButton}
onClick={removeItem} // (2)
>❌</button>
</li>
</>
)
}
TodoItem.PropTypes = {
todoItem: PropTypes.shape({
id: PropTypes.number.isRequired,
todo: PropTyes.string.isRequired,
}),
todoList: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
todo: PropTyes.string.isRequired,
})
),
setTodoList: PropTypes.func.isRequired,
};
export default TodoItem;
(1) click하는 태그의 부모요소를 찾는다.
(1)-1 부모요소를 삭제한다.
(2) ❌버튼을 클릭하면 removeItem 메서드가 실행
// TodoItem.js
import itemStyle from '../Css/TodoItem.module.css';
import PropTypes from 'prop-types';
import { useState } from 'react';
function TodoItem({ todoItem, todoList, setTodoList }){
...
const [edit, setEdit] = useState(false); // (1)
function editItemState(){ // (2)
setEdit(true); // (2)-1
}
return(
<>
<li className={itemStyle.item}>
<input type='checkbox' />
<span>{ todoItem }</span>
<button
type='button'
className={itemStyle.editButton}
onClick={editItemState} // (3)
>🔺</button>
<button
type='button'
className={itemStyle.deleteButton}
onClick={removeItem}
>❌</button>
</li>
</>
)
}
TodoItem.PropTypes = {
...
};
export default TodoItem;
(1) edit상태를 담을 useState 생성 (기본값 false)
(2) edit상태를 false에서 true로 변환하는 메서드 생성
(2)-1 setEdit setter함수를 사용하여 true로 변환
(3) 🔺 버튼을 클릭할 때 editItemState 메서드가 동작
// TodoItem.js
import itemStyle from '../Css/TodoItem.module.css';
import PropTypes from 'prop-types';
import { useState } from 'react';
function TodoItem({ todoItem, todoList, setTodoList }){
...
const [edit, setEdit] = useState(false);
function editItemState(){
setEdit(true);
}
return(
<>
<li className={itemStyle.item}>
<input type='checkbox' />
<span>{ todoItem }</span>
{ // (1)
edit === false ?
(
<button
type='button'
className={itemStyle.editButton}
onClick={editItemState}
>🔺</button>
) :
(
<button
type="button"
className={itemStyle.editButton}
>✔️</button>
)
}
<button
type='button'
className={itemStyle.deleteButton}
onClick={removeItem}
>❌</button>
</li>
</>
)
}
TodoItem.PropTypes = {
...
};
export default TodoItem;
(1) 삼항연산자를 사용하여 edit이 false일 경우 🔺버튼, edit이 true일 경우 ✔️버튼 → toggle 버튼
// TodoItem.js
import itemStyle from '../Css/TodoItem.module.css';
import PropTypes from 'prop-types';
import { useState } from 'react';
function TodoItem({ todoItem, todoList, setTodoList }){
...
const [edit, setEdit] = useState(false);
const [newTodo, setNewTodo] = useState(todoItem.todo); // (1)
function editItemState(){
setEdit(true);
}
function editCompleteItem(){ // (2)
const newEditTodoList = todoList.map((item) => ({ // (2)-1
...item, // (2)-2
todo: item.id === todoItem.id ? newTodo : item.todo // (2)-3
}));
setTodoList(newEditTodoList); // (3)
setEdit(false); // (4)
}
return(
<>
<li className={itemStyle.item}>
<input type='checkbox' />
<span>{ todoItem }</span>
{
edit === false ?
(
<button
type='button'
className={itemStyle.editButton}
onClick={editItemState}
>🔺</button>
) :
(
<button
type="button"
className={itemStyle.editButton}
onClick={editCompleteItem} // (5)
>✔️</button>
)
}
<button
type='button'
className={itemStyle.deleteButton}
onClick={removeItem}
>❌</button>
</li>
</>
)
}
TodoItem.PropTypes = {
...
};
export default TodoItem;
(1) 수정할 todo를 담을 useState를 생성 (기본값은 todoItem.todo(원래담겨있던내용))
(2) editCompleteItem 메서드 생성
(2)-1 todoList을 반복하는 newEditTodoList 객체 생성
(2)-2 todoList에 있던 기존의 item들
(2)-3 새로운 item을 넣음(삼항연산자 사용)
(3) 새로운 리스트를 넣어줌
(4) edit의 상태를 false로 변경
(5) ✔️버튼 클릭시 (2)의 editCompleteItem 메서드 실행
// TodoItem.js
import itemStyle from '../Css/TodoItem.module.css';
import PropTypes from 'prop-types';
import { useState, useRef, useEffect } from 'react';
function TodoItem({ todoItem, todoList, setTodoList }){
...
const [edit, setEdit] = useState(false);
const [newTodo, setNewTodo] = useState(todoItem.todo);
const editInputRef = useRef(null); // (1)
function editItemState(){
setEdit(true);
}
function editCompleteItem(){
const newEditTodoList = todoList.map((item) => ({
...item,
todo: item.id === todoItem.id ? newTodo : item.todo
}));
setTodoList(newEditTodoList);
setEdit(false);
}
function editInput(e){ // (2)
e.target.value;
}
useEffect(() => { // (3)
if(edit == true){ // (3)-1
editInputRef.current.focus(); // (3)-2
}
}, [edit]); // (3)-3
return(
<>
<li className={itemStyle.item}>
<input type='checkbox' />
{
edit === true ? // (4)
(
<input
type='text'
className={itemStyle.editInputbox}
value={newTodo}
onChange={editInput} // (5)
ref={editInputRef} // (6)
/>
) :
(
<span>{ todoItem }</span>
)
}
{
edit === false ?
(
<button
type='button'
className={itemStyle.editButton}
onClick={editItemState}
>🔺</button>
) :
(
<button
type="button"
className={itemStyle.editButton}
onClick={editCompleteItem}
>✔️</button>
)
}
<button
type='button'
className={itemStyle.deleteButton}
onClick={removeItem}
>❌</button>
</li>
</>
)
}
TodoItem.PropTypes = {
...
};
export default TodoItem;
(1) useRef Hook을 사용하여 editInputRef 객체 생성
(2) input의 값이 변할때마다 값을 가져오는 editInput 메서드 생성
(3) useEffect Hook 사용
(3)-1 edit의 상태가 true일 때
(3)-2 editInputRef를 가르키는 DOM을 focus
(3)-3 edit이 실행될 때 마다
⭐ useEffect()?
→ 컴포넌트가 렌더링된 이후 다양한 side effect를 표현할 수 있다.
→ class 생명주기 메서드인 componentDidMount와 componentDidUpdate, componentWillUnmount가 합쳐진 것으로 생각해도 좋다.
→ effect를 실행하고 이를 정리(clean-up)하는 과정을 딱 한 번씩만 실행하고 싶다면, 빈 배열([ ])을 두 번째 인수로 넘기면 된다.
출처 : https://ko.reactjs.org/docs/hooks-effect.html
(4) 변경될 input이 보여질지 원래의 todo내용이 보여질지 삼항연산자를 사용
(5) input의 내용이 바뀔 때 마다 (2)의 editInput 메서드 사용
(6) ref를 사용하여 input에 focus되도록