Front-End/React

[React] event system

engine 2022. 4. 8. 17:13

1. 이벤트 이름은 카멜 표기법으로 작성

2. 이벤트에 실행할 자바스크립트 코드를 전달하는 것이 아니라, 함수 형태의 값 전달

3. DOM 요소에만 이벤트를 설정할 수 있음

(컴포넌트에는 이벤트를 붙일 수 없음)

이벤트 핸들러의 this

이벤트에 실행할 자바스크립트 코드를 전달하는 것이 아니라, 함수 형태의 값을 전달

// rendering
render() {
        return (
            <>
                <h1>event</h1>
                <input type="text" onChange={this.handleChange}/>
                <div/>
                <button onClick={this.handleClick}>button</button>
            </>
        )
    }
// method
constructor(props) {
        super(props);
        this.handleChange = this.handleChange.bind(this);
        this.handleClick = this.handleClick.bind(this);
    }
    handleChange() {
        ...
    }
    handleClick() {
        ...
    }

렌더링하는 input과 button에 각각 handleChange와 handleClick 메소드를 이벤트 핸들러 붙임

 

생성자 부분에서 의아하고 비효율적인 부분을 볼 수 있음

그것은 바로 this.handleChange = this.handleChange.bind(this); 이부분이다.

현재 this의 메소드에 현재 this를 바인딩한다..? 뭐하는 짓이지? 싶겠지만 실제로 저 구문을 빼고 이벤트를 테스트해보면 this를 제대로 잡지 못한다.

간단하게, 자바스크립트에서 함수의 this 누가 호출하느냐에 따라서 바뀌게 되는데 렌더링 과정에서 불러온 이벤트 함수는 EventPractice Class가 부르는 것이 아닌 window객체에서 부르고 있기 때문에 명시적으로 this를 다시 바인딩해주어야 제대로 작동 하는 것

화살표 함수(Arrow Function)

그렇다면 우리는 매번 이벤트 함수를 호출할 때 마다 저런식으로 생성자 함수에서 this를 바인딩해 주어야 하는 걸까?

// 메소드 부분
        handleClick =()=> {
                ...
    }

    handleChange  =()=> {
                ...
    }

그렇지 않다 바로 메소드를 화살표 함수로 정의해 주면 this가 끊기는 문제를 해결할 수 있다.

화살표 함수는 부모의 this를 상속받기 때문에 우리 this를 바인딩해 줄 필요가 없다.

여러개의 이벤트

// state 선언
        state = {
        username : '',
        message : '',
    }
// 렌더링
<input type="text" name="username" value={this.state.username} 
            onChange={this.handleChange}/>
<div/>
<input type="text" name="message" value={this.state.message}
            onChange={this.handleChange}/>

여기 각각 유저 네임과 메시지를 입력 받는 두개의 인풋이 존재한다.

두개의 인풋에서 사용되는 handleChange는 각각 인풋의 벨류값을 받아서 state를 업데이트 해주는 함수

하는 역할이 비슷한데 바꿔야하는 state값이 다르다 이럴 땐 어떻게 해주어야 코드의 중복을 피할 수 있을까

// 메소드 선언
        handleChange =(e)=> {
        this.setState( {
            [e.target.name] : e.target.value
        })
    }

input의 이벤트 객체에서 name 값을 가져온 뒤 키값으로 잡고 벨류값으로 업데이트 해주면 된다.

대충 만든 전체 코드

class EventPractice extends Component{
    state = {
        username : '',
        message : '',
    }

    render() {
        return (
            <>
                <h1>event</h1>
                <input type="text" name="username" value={this.state.username} onChange={this.handleChange}/>
                <div/>
                <input type="text" name="message" value={this.state.message} onChange={this.handleChange}/>
                <button onClick={this.handleClick}>button</button>
            </>
        )
    }

    handleClick =(e)=> {
        console.log(`username ${this.state.username}`);

        console.log(`msg ${this.state.message}`);
    }

    handleChange =(e)=> {
        this.setState( {
            [e.target.name] : e.target.value
        })
    }
}

함수형 컴포넌트

위에서 클래스형으로 만든 것을 그대로 옮겨보자

useState 다중사용

export const EventPracticeFunctional = ()=>{
    // 인풋의 값이 적을 때는 아래와 같은 하드코딩도 나쁘지 않음.
    const [username, setUsername] = useState('John');
    const [message, setMessage] = useState('Hi');

    const usernameChange = (e)=> {
        setUsername(e.target.value);
    }

    const messageChange = e => {
        setMessage(e.target.value);
    };

    const handleClick = e => {
        console.log(`${username} say ${message}`);
    };

    return (
        <div>
            <input type="text" name={'username'} value={username} onChange={usernameChange}/>
            <div/>
            <input type="text" name={'message'} value={message} onChange={messageChange}/>
            <button onClick={handleClick}> button </button>
        </div>
    );
}

이 함수형 컴포넌트는 useState를 이용해서 위의 클래스 컴포넌트와 똑같이 작동

하지만 거슬리는 부분이 존재

usernameChange와 messageChange 메소드의 코드 중복이 매우 보기 싫은 상태

함수형 컴포넌트에서는 이런 경우 어떻게 코드 중복을 줄일까?

한번의 useState


export const EventPracticeFunctionalMulti = ()=>{
    const [form, setForm] = useState({
        username : 'John',
        message : 'Hi',
    })

    const {username,message} = form;

    const changeHandler = (e) => {
        const nextForm = {
            ...form,
            [e.target.name] : e.target.value,
        };
        setForm(nextForm);
    }

    const handleClick = (e) => {
        console.log(username,message);
    };

    return (
        <div>
            <input type="text" name={'username'} value={username} onChange={changeHandler} />
            <div/>
            <input type="text" name={'message'} value={message} onChange={changeHandler} />
            <button onClick={handleClick}> button </button>
        </div>
    );
}

useState에 문자열이 아닌 객체를 넣어주어서 관리하는 방법이다.

changeHandler에서는 업데이트를 위한 객체를 선언한 뒤 원래있던 객체를 복사하고

const nextForm

nextForm = {
            ...form

원소의 이름과 값을 이벤트 객체에서 받아와 덮어 씌운 뒤

[e.target.name] : e.target.value,

useState를 이용해 값을 업데이트

setForm(nextForm);