📌 이벤트(사용자 액션) 처리하기
- 사용자 액션(행동) → UI 업데이트
✅ 컴포넌트 영역(순수)와 이벤트 영역(부수)
⏩ 컴포넌트 함수 영역
- 사용자가 행동하지 않아도 진행되는 부분
- 렌더링 시점에 함수가 실행이 되는 부분
👉 순수 함수 사용
⏩ 이벤트 핸들러 함수 영역
- 사용자가 행동을 취해야 진행되는 부분
- 사이드 이펙트(부수 효과)를 위한 최고의 위치
- 렌더링 시점에 함수가 실행되지 않는 부분
👉 사이트 이펙트(부수효과) 함수 사용
ex) 프로필 목록 컴포넌트
- 서버에 데이터를 요청해 응답 받아 데이터를 가져와야 한다면 "부수 효과"
- 응답 받은 데이터를 기반으로 리스트 렌더링 하는 것은 "순수 함수"
📢 컴포넌트 함수 내부의 이벤트 핸들러 함수에서 부수효과를 다루는 이유
👉 렌더링 될 때 이벤트 핸들러 함수가 자동으로 실행되는 것이 아니고, 사용자가 행동을 하여 이벤트가 발생할 때에 비로소 이벤트 핸들러 함수가 실행되므로 컴포넌트 영역은 순수하다
- 컴포넌트 함수 내부에 이벤트 핸들러 함수가 존재하더라도, 컴포넌트의 출력 값인 JSX 마크업 생성에 영향을 주지 않기 때문에 '같은 입력 👉 같은 출력' 이라는 순수 함수의 조건을 만족
- 컴포넌트 함수가 실행되는 동안(렌더링하는 동안) 외부의 상태를 변경하지 않는다는 조건도 만족
✅ 이벤트 핸들러 추가
- 함수를 정의하고 JSX 태그에 prop 형태로 전달
- 이벤트 핸들러 함수명은 handle로 시작한다.
(ex. onClick={handleClick}, onMouseEnter={handleMouseEnter})
⏩ 이벤트 핸들러
- 컴포넌트 내부(바디(return 위에))에 정의
- 컴포넌트 내부에 이벤트 핸들러 함수 정의
- JSX의 태그 prop으로 이벤트 핸들러 추가
// 1. 컴포넌트 바디(함수 몸체) 내부
// 이벤트 핸들러 정의
const handleClickFirstLink = (e) => {
e.preventDefault();
const firstLink = document.querySelector('[href="#jsx-markup"]');
firstLink.dataset.element = 'jsx-markup';
};
// 사이드 이펙트(부수 효과) 처리 함수
const handleClickLastLink = (e) => {
e.preventDefault();
const lasatLink = document.querySelector('[href="#responding-to-events"]');
lasatLink.dataset.element = 'responding-to-events';
};
// 2. JSX 내부
return (
<nav className="NavContents" aria-label="학습 주제 탐색">
<a
href="#jsx-markup"
onClick={handleClickFirstLink} // DOM 월드에서 사용자에 의해 실행
>
JSX 마크업
</a>
<a
href="#responding-to-events"
className="active"
onClick={handleClickLastLink}
>
이벤트 응답
</a>
</nav>
);
⏩ 인라인 핸들러
- JSX 내부에 정의
- 짧은 함수들을 정의할 때 편리
<button onClick={function handleClick() {
alert('You clicked me!');
}}>
✅ 이벤트 핸들러에서 props 읽기
- 이벤트 핸들러는 컴포넌트 내부에서 선언되기에 해당 컴포넌트의 prop에 접근할 수 있다.
1. 상위 컴포넌트에서 이벤트 핸들러를 prop으로 전달하기
function RespondingToEvents() {
// 함수 지역 변수 (데이터)
let message = '김사부! "집중~~"';
// 함수 내부의 지역 함수 (데이터)
const printMessage = () => console.log(message);
const updateMessage = (addMessage) => {
message += addMessage;
};
return (
<div className="ViewRespondingToEvent">
<!-- 컴포넌트 내부의 함수를 prop으로 전달 -->
<EventHandlerProp onPrintMessage={updateMessage} />
</div>
);
}
2. 하위 컴포넌트에서 이벤트 핸들러를 prop으로 받아오기
// 하위 컴포넌트
// 상위 컴포넌트에서 전달한 함수를 prop으로 받아옴
function EventHandlerProp({ onPrintMessage }) {
const handleEnter = () => {
console.log('enter');
onPrintMessage?.(' ⭐️'); // '김사부! 집중~~ ⭐️'
};
const handleLeave = () => {
console.log('leave')
};
return (
<details>
<summary onPointerEnter={handleEnter} onPointerLeave={handleLeave}>
<b>이벤트 핸들러 추가하기</b>
</summary>
<p>
이벤트 핸들러 추가를 위해서는 먼저 함수를 정의하고,
이를 적절한 JSX 요소에 prop으로 전달해야 합니다.
</p>
</details>
);
}
export default EventHandlerProp;
✅ 이벤트 전파(Event Propagation)
: 이벤트가 발생했을 때 이벤트가 DOM 트리에서 어떻게 전달되는지를 설명하는 과정
- 버블링(Bubbling)
- 캡쳐(Capturing)
import LayoutBox from './LayoutBox';
function EventPropagation() {
function returnHandlePrint(color) {
return function handlePrint(e) {
console.log(color, e.target);
};
}
return (
<details>
<LayoutBox style={styles.cyan} onClick={returnHandlePrint('cyan')}>
<LayoutBox
style={styles.magenta}
onClick={returnHandlePrint('magenta')}
>
<LayoutBox
style={styles.yellow}
onClick={returnHandlePrint('yellow')}
>
<LayoutBox
style={styles.purple}
onClick={returnHandlePrint('purple')}
/>
</LayoutBox>
</LayoutBox>
</LayoutBox>
</details>
);
}
const styles = {
cyan: {
'--color': 'var(--cyan)',
},
magenta: {
'--color': 'var(--magenta)',
},
yellow: {
'--color': 'var(--yellow)',
},
purple: {
'--color': 'var(--purple, #7423f6)',
},
};
export default EventPropagation;
⏩ 이벤트 위임(Event Delegation)
: 이벤트 전파의 개념을 활용하여 부모 요소에서 자식 요소의 이벤트를 처리하는 방법
- 자식 요소 각각에 이벤트 리스너를 추가하는 대신, 부모 요소에 이벤트 리스너를 추가하여 자식 요소에서 발생한 이벤트를 처리
⏩ 이벤트 전파 중지
event.stopPropagaion()
- 이벤트 핸들러가 상위 태그로 버블링되지 않도록 막는다.
(해당 이벤트에만 실행)
⏩ 전파 대안으로 핸들러 전달
- 부모 컴포넌트가추가적인 동작하게 하여 자식 컴포넌트가 이벤트를 처리할 수 있도록 한다.
- 전파와는 다르게 자동으로 동작하지 않는다.
⏩ 기본 작동 방지
event.preventDefault()
- 기본 브라우저 동작을 막는다.