버튼을 눌렀더니 화면이 안 움직여요 (콜백함수, 이벤트 리스너)

코린·2024년 1월 24일
0

리액트

목록 보기
21/22
post-thumbnail

코드

import { useSelector, useDispatch } from "react-redux";
import Stack from "@mui/material/Stack";
import Button from "@mui/material/Button";
import { decreaseCount, increaseCount } from "../store/cart/cartSlice";
import { selectCartTotal } from "../store/cart/cartSlice";

function Cart() {
  const dispatch = useDispatch();
  const list = useSelector((state) => state.cart);
  const total = useSelector(selectCartTotal);

  const handelPlus = (id) => {
    dispatch(increaseCount(id));
  };

  const handelMinus = (id) => {
    dispatch(decreaseCount(id));
  };

  return (
    <Stack spacing={2} direction="column" margin={5} alignItems="center">
      {list.map((data) => (
        <Stack key={data.id} spacing={2} margin={5}>
          <div>{"상품 이름: " + data.title}</div>
          <div>{"가격: " + data.price}</div>
          <Stack spacing={2} direction="row">
            <Button variant="outlined" onClick={handelPlus(data.id)}>
              +
            </Button>
            <div>{"갯수: " + data.count}</div>
            <Button variant="outlined" onClick={handelMinus(data.id)}>
              -
            </Button>
          </Stack>
        </Stack>
      ))}
      <div>상품 총액: {total}</div>
    </Stack>
  );
}

export default Cart;

위와 같이 작성했더니 화면이 동작하지 않게되었습니다...

이유?

버튼 클릭 시 함수가 즉시 호출되는 형태로 작성
-> 이를 해결하기 위해서는 콜백 함수를 사용하여 이벤트 핸들러를 올바르게 작성해야 합니다.

해결!

<Button variant="outlined" onClick={()=>handelMinus(data.id)}>
              -
</Button>

콜백함수로 변경!

콜백함수와 직접 호출

onClick={handelPlus(data.id)}는 함수를 직접 호출하는 것입니다.
onClick={()=>handelPlus(data.id)}는 콜백 함수로 작성한 것입니다.

  1. 실행 시점
  • 직접 호출
    • 렌더링 시점에 함수가 호출, 이벤트가 발생 하기 전에 함수가 실행
  • 콜백
    • 클릭 이벤트가 발생했을 경우에만 함수가 실행됨
  1. 클로저
  • 직접 호출

    • 클로저 문제가 발생할 수 있음
      • 클로저란, 외부 함수의 실행이 끝나 함수가 소멸되어도 내부 함수가 외부 함수의 변수에 접근할 수 있는 것
      • 클로저문제란, 함수가 선언될 당시의 상태를 유지하면서 실행되는 현상
  • 콜백

    • 클로저 문제 피할 수 있음

      클로저 예시상황

      반복문 안에서 이벤트 핸드러를 등록할 때 직접호출을 사용하게 되면 반복문의 마지막 값만을 참조하게 됨

      for (var i = 0; i < 5; i++) {
      setTimeout(function() {
        console.log(i);
      }, 1000);
      }
      

      위의 결과는 5가 5번 출력되게 된다.

      이유?

      setTimeout 함수 내부의 콜백 함수가 외부 함수인 for 루프의 변수 i에 접근하기 때문입니다. setTimeout 함수는 비동기적으로 실행되는데, 콜백 함수가 실제로 실행되는 시점은 for 루프가 이미 완료된 이후입니다. 따라서 setTimeout 함수 내부의 콜백 함수는 for 루프가 종료된 후에 실행되며, 이때 i의 값은 이미 5가 되어있습니다.

      해결!

      for (var i = 0; i < 5; i++) {
      (function(j) {
        setTimeout(function() {
          console.log(j);
        }, 1000);
      })(i);
      }

      콜백함수로 작성해주면 됩니다.

  1. 렌더링
    • 직접호출로 작성하게 되면 함수를 호출하게 되고 상태 값이 변하게 됨
      • 이는 과한 렌더링을 유발

참고블로그

이벤트 리스너(event listener)와 콜백함수(call back function)
[JavaScript]Callback vs Promise 자바스크립트 콜백함수 vs 프로미스 예시, 비교
콜백과 이벤트
생활코딩 댓글
클로저
[JavaScript] 클로저 (Closure)

profile
안녕하세요 코린입니다!

1개의 댓글

comment-user-thumbnail
2024년 3월 18일

킹받네요!

답글 달기