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 요소 안에 저장해 주기 위해서 사용합니다.

     

    데이터 속성에 대한 더 자세한 설명은 아래 링크를 참조하세요!

     

     

    데이터 속성 사용하기 - Web 개발 학습하기 | MDN

    HTML5 특정 요소와 연관되어 있지만 확정된 의미는 갖지 않는 데이터에 대한 확장 가능성을 염두에 두고 디자인되었습니다. data-* 속성은 표준이 아닌 속성이나 추가적인 DOM 속성, Node.setUserData

    developer.mozilla.org

     

    이벤트가 발생하는 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()를 넣는 꼴이 되므로 사용할 수 없습니다.)

     

    진리의 mdn

     

    bind의 일반적인 기능인 this 변경의 경우 ES6의 arrow function을 사용할 경우 정상적으로 작동하지 않습니다.

    실행 컨텍스트가 생성될 때 this binding이 이루어지지 않고 어떻게 호출되었냐에 따라 동적으로 결정되기 때문이죠!

     

    하지만 this binding과는 무관하게 매개변수는 전달할 수 있습니다.

    이걸 이용해서 별도의 익명함수를 생성하지 않고도 매개변수를 전달하는 것이죠!

     

    onClick={deleteTodo.bind(this, todo.num)}

     

    반응형
    • 네이버 블로그 공유하기
    • 네이버 밴드에 공유하기
    • 페이스북 공유하기
    • 카카오스토리 공유하기