리액트 공식 문서 '상호작용 추가하기' 파트
1번째, 2번째 섹션 발표 자료 정리
이벤트 핸들러
사용자의 상호작용에 반응하여 자체적으로 발생하는 이벤트를 다루는 함수
const handleChange = () => ~
)<input onChange={handleChange} />
)이벤트 핸들러 함수 네이밍 관습
앞에handle
을 붙인다. (예:handleClick
,handleMouseEnter
등)
Pitfall: 함정 🥅
- 이벤트 핸들러 함수는 즉시 호출되는 게 아니라 '전달'되어야 한다.
<button onClick={handleClick}>
(올바른 형태)<button onClick={handleClick()}>
(틀린 형태)- 이벤트 핸들러 함수의 내부 로직을 인라인으로 전달하려고 할 때는 익명 함수로 호출한다.
<button onClick={() => console.log('handleClick 실행!')}>
(올바른 형태)<button onClick={console.log('handleClick 실행!')}>
(틀린 형태)
// 위 handleClick 함수의 생김새
const handleClick = () => {
console.log('handleClick 실행!');
}
이벤트 핸들러 네이밍 관습
- 이벤트 핸들러는 JSX에 props로 들어가는 속성이고, 이벤트 핸들러 함수는 이 props의 중괄호 안에 들어가는 함수를 의미한다.
- 이벤트 핸들러는 관습적으로
on
으로 시작하고 이후 카멜 케이스로 작성한다.
onChange
, handleChange
)profile-image.png
, /api/main/sales-mode
)const Sidebar = () => ...
)CARD_INFO
) const handleToolbarClick = () => console.log('Toolbar 클릭!');
const handleButtonClick = () => console.log('Button 클릭!');
<Toolbar onClick={handleToolbarClick}>
<Button onClick={handleButtonClick}>클릭</Button>
</Toolbar>
Button
을 클릭하면 콘솔에 'Button 클릭!'이 출력된 뒤 상위 컴포넌트 Toolbar
에 있는 'Toolbar 클릭!' 콘솔도 실행됨e.stopPropagation()
를 선언하여 버블링을 막는다.e.stopPropagation()
: e(event) 객체의 메서드 ('전파를 멈추라'는 뜻)이벤트 전파가 발생하는 이유?
브라우저에서는 기본적으로 이벤트 버블링(전파)이 발생한다.
그러면 이러한 이벤트 버블링은 왜 발생하는 것일까?
(ChatGPT를 통해 추가 자료 조사)
요약: 이벤트 위임을 위해서이다.
- 이벤트 위임이란 상위 DOM에 이벤트를 등록하면 하위 DOM에 일일이 이벤트를 등록하지 않아도 되는 것을 의미한다. 하위에서 이벤트가 발생해도 상위까지 버블링되어 올라가기 때문에 가능한 일이다.
- 이벤트 위임이 필요하지 않은 경우라면
e.stopPropagation()
으로 이벤트 버블링을 막아주면 된다.
- 이렇게 이벤트 버블링이 필요하지 않은 경우
예: Card 컴포넌트 안에 Like(좋아요) 하트 컴포넌트가 있는데 하트 컴포넌트만 눌리게 하고 싶은 경우
Pitfall: 함정 🥅
React에서
onScroll
을 제외한 모든 이벤트는 전파된다.
추가:onScroll
외에 인풋focus
이벤트도 전파되지 않는다.
e.stopPropagation()
) 또는 브라우저 기본 동작 중지(e.preventDefault()
) 등이 가능Deep Dive 🌊
캡쳐링(capturing) 동작
- 캡쳐링은 버블링과 반대로 상위에서 하위로 이벤트가 전파되는 현상이다.
onClickCapture
를 이벤트 핸들러로 넣어주면 의도적으로 캡쳐링 이벤트를 일으킬 수 있다.- 하지만 거의 안 씀 (라우터나 분석에 잠깐 쓰는 정도)
function Button({ onClick, children }) {
return (
<button onClick={(e) => {
e.stopPropagation();
onClick();
}}>
{children}
</button>
)
}
e.stopPropation()
으로 막을 수 있다.브라우저 기본 동작 방지하기
e.preventDefault()
: 기본 브라우저 동작이 있을 때 사용하면 기본 동작을 막는다.- form의 submit 이벤트의 기본 동작인 새로고침을 막을 때 많이 사용하고, 그 외 input checkbox의 클릭 시 체크되는 기본 동작 등을 막을 수 있다.
import { sculptureList } from './data.js';
export default function Gallery() {
let index = 0;
function handleClick() {
index = index + 1;
}
let sculpture = sculptureList[index];
return (
...
);
// '다음'을 눌러 handleClick이 호출되고 index 변수가 바뀌어도
// 이미지가 변경(= 화면이 리렌더링)되지 않는다.
=> 이 두 가지를 모두 해결해주는 것이 리액트의 hook 중 하나인 useState
// 원래 반환되는 형태
const result = useState(0);
// 반환된 값을 배열 비구조화 할당으로 꺼냄 (= 보통 사용하는 useState)
const [index, setIndex] = useState(0);
배열 비구조화 할당 vs. 객체 비구조화 할당
- 배열 비구조화 할당
- 배열을 사용하여 변수를 할당했을 때 각 요소를 사용자 지정 변수로 꺼내서 출력할 수 있다.
- 객체는 key로 정해져 있기 때문에 이 부분이 힘들다.
=> useState에서 배열 비구조화 할당을 쓰는 이유 (사용자가 마음대로 변수를 지정해서 꺼내 쓸 수 있다)const numbers = [1, 2, 3]; const [a, b, c] = numbers; console.log(a); // 1 console.log(b); // 2 console.log(c); // 3
- 객체 비구조화 할당
- 변수에 객체를 할당하고 객체의 key를 중괄호 안에 꺼내서 value 출력에 사용한다.
const person = { name: 'John', age: 30 }; const { name, age } = person; console.log(name); // 'John' console.log(age); // 30
🌊 Deep Dive: 어떤 state를 반환할지 리액트가 어떻게 인식할까?
- 따로 식별자가 있는 것은 아니고 hook이 호출되는 순서에 의존한다.
- 최상위 수준에서만 hook을 호출하면 항상 같은 순서로 호출되어 문제 없이 작동
=> 최상위 수준에서만 hook을 호출해야 하는 이유!
=> 모듈 상단에 선언된 일반 변수와의 차이점이라고 할 수 있다.
(ChatGPT로 추가 자료 조사)
Hook이 지켜야 하는 규칙
- 네이밍이 'use'로 시작해야 리액트가 hook으로 인지한다.
- 커스텀 hook이든, 리액트에서 제공하는 hook이든 상관 없이
- 컴포넌트 함수 내 최상위 레벨에 선언되어야 한다.
- 최상위 파일 index.js라는 뜻이 아니라 컴포넌트 함수 내에서의 최상위
= 함수나 조건문, 반복문 같은 중괄호 내에서 호출되면 안 된다- 일반 자바스크립트 함수가 아닌 리액트 컴포넌트 함수 내에서만 호출되어야 한다.
- 선언된 순서에 영향 받는다.
- 재료인 useState 먼저 선언 후 useEffect를 선언
- useEffect가 한 컴포넌트 내에서 여러 개인 경우에 순서를 고려하지 않으면 버그 발생