CRA로 간단한 투두리스트를 만들다가 onClick이벤트에 함수를 전달해야 할 일이 생겼는데, 어떻게 해야할 지 잘 모르겠어서 여러 가지로 찾아보면서 알게 된 자료를 정리해봅니다.
요약하면 익명함수, data-set, bind를 사용할 수 있습니다!
목차
0. 익명 함수 사용하기
처음에 생각했던 것은 todo.num이라는 key값을 이벤트함수에 전송해서 그곳에서 전체 todos안에서 해당하는 key값과 일치하는 key값을 가진 todo의 done값을 바꾸거나(상태 수정) todo를 삭제하는(삭제) 기능을 구현하려고 했습니다.
그런데 생각해보니 매번 onClick = {함수명} 형식으로 이벤트에 해당하는 함수를 작동시키다보니, 이벤트(e) 이외에 다른 매개변수를 전달할 수 없을까 생각했습니다.
고차함수를 이용하여 todo.num과 삭제 혹은 상태변경을 원하는 요소의 key값을 비교해야 하는데, 그걸 위해서는 key값을 전달해주어야 했기 때문에 그런 생각을 하게 되었던 것이죠!
다양한 방법이 있었지만, 그 중에서도 가장 쉽게 사용할 수 있는 방법이 바로 익명함수를 사용하는 방법입니다.
// onClick={deleteTodo}
onClick={() => deleteTodo(todo.num)}
위 예시처럼 onClick이벤트가 발생했을 때 새로운 익명함수를 만들어서 deleteTodo함수를 작동하게 만들 수 있습니다.
다만 이 방법은 매번 이벤트가 작동해서 콜백함수가 동작할 때 익명함수가 생성되고 호출되는 과정이 생길 수 있다는 단점이 있습니다. (차이는 크지 않지만, 최적화에 있어서 최적의 방식은 아니라는 말이 되겠죠?
첫 코드이므로 제가 만든 List 컴포넌트의 전문을 가져왔습니다.
앞으로 나오는 추가적인 예시들에서는 필요한 부분만 추출해서 작성하도록 하겠습니다!
import "./List.css";
import React from "react";
const List = ({ todos, setTodos, setToast, onToast }) => {
const deleteTodo = (target) => {
const newtodos = todos.filter((todo) => todo.num !== target);
setTodos(newtodos);
setToast("삭제되었습니다.");
onToast();
};
const changeStatus = (target) => {
setTodos(
todos.map((todo) =>
todo.num === target ? { ...todo, done: !todo.done } : todo
)
);
setToast("상태가 변경되었습니다.");
onToast();
};
const todoList = todos.map((todo) => (
<div key={todo.num}>
<input type="checkbox" onClick={() => changeStatus(todo.num)}></input>
<span style={todo.done ? { textDecoration: "line-through" } : {}}>
{todo.what}
</span>
<button
type="submit"
className="xbtn"
onClick={() => deleteTodo(todo.num)}
>
X
</button>
</div>
));
return <>{todoList}</>;
};
export default List;
1. data-set 사용하기
또 다른 방법으로는 HTML5부터 추가된 개념인 데이터 속성을 사용할 수 있습니다.
데이터 속성은 특정한 데이터를 DOM 요소 안에 저장해 주기 위해서 사용합니다.
데이터 속성에 대한 더 자세한 설명은 아래 링크를 참조하세요!
이벤트가 발생하는 DOM 요소에 미리 제가 원하는 key값을 넣어두고, onClick에 의해서 이벤트가 감지되서 함수가 작동하게 되면 내부에서 e.target.dataset을 통해서 접근하는 방식이죠.
이때 주의해야 할 점은 data-set에는 다른 자료형을 넣어도 문자열로 변환되기 때문에 사용할 때 원하는 자료형으로 변경시켜야 합니다.
아래와 같이 코드를 짜고 onClick에 의해 deleteTodo가 실행되면 data-num에 있던 정보가 target으로 넘어가서 비교되는 방식으로 작동하게 됩니다.
const deleteTodo = (e) => {
const target = +e.target.dataset.num;
const newtodos = todos.filter((todo) => todo.num !== target);
setTodos(newtodos);
setToast("삭제되었습니다.");
onToast();
};
<button type="submit" className="xbtn" data-num={todo.num} onClick={deleteTodo}>X</button>
2. bind 사용하기
bind는 일반적으로는 this를 변경하는 데에 사용됩니다.
하지만 this 뒤로는 매개변수를 전달할 수도 있죠. (이것을 인수 바인딩이라고 부릅니다.)
(TMI : call과 apply는 동일하게 this를 변경하는 데에 사용되지만, 함수가 바로 실행된다는 차이점이 있습니다. 따라서 저와 같은 케이스에서 onClick의 매개변수로 deleteTodo()를 넣는 꼴이 되므로 사용할 수 없습니다.)
bind의 일반적인 기능인 this 변경의 경우 ES6의 arrow function을 사용할 경우 정상적으로 작동하지 않습니다.
실행 컨텍스트가 생성될 때 this binding이 이루어지지 않고 어떻게 호출되었냐에 따라 동적으로 결정되기 때문이죠!
하지만 this binding과는 무관하게 매개변수는 전달할 수 있습니다.
이걸 이용해서 별도의 익명함수를 생성하지 않고도 매개변수를 전달하는 것이죠!
onClick={deleteTodo.bind(this, todo.num)}
최근댓글