리액트 기본 배우기 -이벤트 처리-

1. DOM의 이벤트

 

DOM에서는 클릭 이벤트를 처리할 함수를 onclick을 통해 전달시킴

 

<button onclick="activate()">
    Activate
</button>

 

위 코드는 버튼이 눌리면 activate()라는 함수를 호출하도록 되어 있다

 

 

2. 리액트에서 이벤트

 

onclick 대신 onClick으로 카멜 표기법

 

DOM에서는 이벤트 처리 함수를 문자열 그대로 전달하지만, 리액트에서는 {}를 사용해서 함수 그대로 전달

 

<button onClick={activate}>
    Activate
</button>

 

 

3. 이벤트 핸들러

 

어떤 이벤트가 발생했을 때 해당 이벤트를 처리하는 함수

 

혹은 이벤트가 발생하는 것을 계속 듣고 있는다는 의미로 이벤트 리스너(event listener)라고도 부름

 

class Toggle extends React.Component {
    constructor(props){
    super(props);

    this.state = {isToggleOn: true}

    //callback에서 `this`를 사용하기 위해 필수적으로 바인딩을 해야함
    this.handleClick = this.handleClick.bind(this)
    }

    handleClick() {
        this.setState(prevState => ({
            isToggleOn: !prevState.isToggleOn
        }))
    }

    render() {
        return (
            <button onClick={this.handleClick}>
                {this.state.isToggleOn ? '켜짐' : '꺼짐'}
            </button>
        )
    }
}

 

위 코드에서는 Toggle이라는 클래스 컴포넌트가 나온다

 

컴포넌트의 state에 isToggleOn이라는 boolean 변수가 하나 있다

 

버튼을 클릭하면 이벤트 핸들러 함수인 handleClick() 함수를 호출하도록 되어 있다

 

여기서 눈여겨볼 곳은 handleClick()함수를 정의하는 부분

 

먼저 handleClick() 함수의 정의 부분은 일반적인 함수를 정의하는 것과 동일하게 괄호와 중괄호를 사용해서 클래스의 함수로 정의

 

이렇게 정의된 함수를 constructor()에서 bind()를 이용해 this.handleClick에 대입해준다

 

this.handleClick = this.handle.Click.bind(this) 부분

 

JSX에서 this의 의미에 대해 유의해야한다.

 

bind를 하는 이유는 자바스크립트에서는 기본적으로 클래스 함수들이 bound되지 않기 때문이다.

 

bind를 하지 않으면 this.handleClick은 글로벌 스코프에서 호출되는데,

 

글로벌 스코프에서 this.handleClick은 undefined이므로 사용할 수가 없다

 

이것은 리액트에만 해당하는 내용이 아닌 자바스크립트 함수의 작동 원리

 

일반적으로 함수의 이름 뒤에 괄호 () 없이 사용하려면 무조건 해당 함수를 bind해줘야함

 

bind가 번거롭다면 다음과 같이 클래스 필드 문법을 사용할 수 있다고함

 

class MyButton extends React.Component {
    handleClick = () => {
        console.log('this is:', this)
    }

    render() {
        return (
            <button onClick={this.handleClick}>
                클릭
            </button>
        )
    }
}

 

bind와 클래스 필드 문법을 사용하지 않으려면 아래와 같이 이벤트 핸들러를 넣는 곳에 arrow function을 넣을 수 있다

 

class MyButton extends React.Component {
    handleClick() {
        console.log('this is:', this)
    }

    render() {
        //이렇게 하면 `this`가 바운드된다.

        return (
            <button onClick={() => this.handleClick()}>
                클릭
            </button>
        )
    }
}

 

하지만 클래스 컴포넌트는 거의 사용하지 않아 다음과 같이 함수 컴포넌트로 바꿀 수 있다

 

function Toggle(props) {
    const [isToggleOn, setIsToggleOn] = useState(true);

    //방법1. 함수 안에 함수로 정의
    function handleClick() {
        setIsToggleOne((isToggleOn) => !isToggleOn)
    }

    //방법2. arrow function을 사용하여 정의
    const handleClick = () => {
        setIsToggleOn((isToggleOn) => !isToggleOn)
    }

    return (
        <button onClick={handleClick}>
            {isToggleOn? "켜짐":"꺼짐"}
        </button>
    )
}

 

위 함수 컴포넌트에서는 이벤트 핸들러를 함수 안에 또 다른 함수로 정의하는 방법, arrow function으로 정의하는 방법이 있다.

 

this를 사용하지 않고 onClick에 곧바로 handleClick로 넘길 수 있다

 

 

3. arguments 전달

 

함수에 전달할 데이터를 arguments라고 부르며 같은 의미로 파라미터, 매개변수라는 용어도 사용

 

예를 들어 특정 사용자 프로필을 클릭했을 때 해당 사용자의 아이디를 매개변수로 전달해서 정해진 작업을 처리해야 하는 경우 매개변수 전달이 필수

 

<button onClick={(event) => this.deleteItem(id,event)}>삭제하기</button>
<button onClick={this.deleteItem.bind(this,id)}>삭제하기</button>

 

위의 코드 두 줄은 모두 동일한 역할

 

하나는 arrow function

 

하나는 Function.prototype.bind를 사용

 

event라는 매개변수는 리액트의 이벤트 객체

 

두 방법 모두 첫번째 매개변수는 id이며, 두번째 매개변수로 event가 전달

 

하지만 arrow function을 사용하는 방법은 명시적으로 event를 전달했지만

 

Function.prototype.bind를 사용한 방법은 event가 자동으로 id이후의 두번째 매개변수로 전달

 

이거는 클래스 컴포넌트에서 사용하는 방식으로 거의 사용하지 않고

 

함수 컴포넌트에서는 다음과 같이 매개변수를 전달 할 수 있다

 

매개변수 순서는 바꿔도 자바스크립트는 알아서 전달 해준것 같았음

 

function MyButton(props) {
    const handleDelete = (id, event) => {
        console.log(id,event.target)
    }

    return (
        <button onClick={(event) => handleDelete(1,event)}>삭제하기</button>
    )
}

 

4. 실습으로 따라하기

 

$npx create-react-app my-app으로 react app 생성

 

src 폴더 내부에 chapter_08이라는 폴더 생성

 

ConfirmButton.jsx라는 이름의 파일 새로 만들고 ConfirmButton이라는 클래스 컴포넌트 작성

 

import React from 'react';

class ConfirmButton extends React.Component {
    constructor(props){
        super(props)

        this.state = {
            isConfirmed: false
        }
        
        this.handleConfirm = this.handleConfirm.bind(this)
    }

    handleConfirm() {
        this.setState((prevState) => ({
            isConfirmed: !prevState.isConfirmed
        }))
    }

    render() {
        return (
            <button
            onClick={this.handleConfirm}
            disabled={this.state.isConfirmed}>

                {this.state.isConfirmed ? '확인됨' : '확인하기'}

            </button>
        )
    }
}

export default ConfirmButton;

 

ConfirmButton 컴포넌트는 확인 여부를 저장하기 위해 state에 isConfirmed 라는 변수를 하나 가지고 있고 초기값은 false이다.

 

그리고 버튼의 onClick 이벤트를 처리하기 위해 이벤트 핸들러로 handleConfirm이라는 함수를 만들어 넣어줬다.

 

여기서는 bind를 사용해서 이벤트 핸들러를 처리함

 

ConfirmButton 컴포넌트를 실제로 화면에 렌더링하기 위해 index.js 파일을 수정

 

import ConfirmButton from './chapter_08/ConfirmButton'

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <ConfirmButton />
  </React.StrictMode>
);

 

npm start 명령어로 실행

 

 

확인 버튼을 누르면 클릭 이벤트가 이벤트 핸들러로 전달되고 isConfirmed의 값이 true로 바뀌면서 아래 그림과 같이 버튼이 비활성화

 

 

이번엔 이벤트 핸들러를 클래스 필드 문법을 사용하여 변경

 

import React from 'react';

class ConfirmButton extends React.Component {
    constructor(props){
        super(props)

        this.state = {
            isConfirmed: false
        }
        
    }

    handleConfirm= () => {
        this.setState((prevState) => ({
            isConfirmed: !prevState.isConfirmed
        }))
    }

    render() {
        return (
            <button
            onClick={this.handleConfirm}
            disabled={this.state.isConfirmed}>

                {this.state.isConfirmed ? '확인됨' : '확인하기'}

            </button>
        )
    }
}

export default ConfirmButton;

 

 

역시 동일한 결과를 얻는다

 

 

 

이번엔 함수 컴포넌트로 변경

 

사실상 함수 컴포넌트만 쓰기 때문에 잘 기억해야

 

const 변수 + useState()로 위에 state 정의하고

 

arrow function으로 event handler 정의하고..

 

onClick에 함수 그 자체를 {}에 전달해주고...

 

import React, {useState} from 'react';

function ConfirmButton(props) {
    const [isConfirmed, setIsConfirmed] = useState(false)

    const handleConfirm = () => {
        setIsConfirmed((prevIsConfirmed) => !prevIsConfirmed)
    }

    return (
        <button onClick={handleConfirm} disabled={isConfirmed}>
            {isConfirmed ? "확인됨" : "확인하기"}
        </button>
    )
}

export default ConfirmButton

 

역시 여전히 동일한 결과를 얻는다

 

 

TAGS.

Comments