이벤트 핸들링하기

조민성·2024년 10월 1일

React

목록 보기
5/9

1. 이벤트 연결하기

  • React는 JavaScript처럼 Camel Case 형태로 함수를 전달해줌.
  • HTML의 경우에는 Camel Case가 아닌, 소문자 onclick에 실행함수를 넣는다.
import {useCallback, useState} from "react";
function handleClick2(e) {
    console.log('click2', e);
}
function App (){
// 이벤트 연결하기
    function handleClick1(e) {  // e = 합성 이벤트 = JavaScript에서 전달받은 원본 이벤트 객체를 '확장'한 객체.
        // 사실상 동일한 이벤트 객체라고 봐도 된다.
        console.log('click1', e);
    }
		//이벤트 핸들러를 만들 때는 react LifeCycle을 고려하기.
    // - 컴포넌트가 리렌더링 되는 경우 컴포넌트 내에서 단순 정의한 함수가 새롭게 만들어져 성능이 떨어짐
    // - 따라서 함수 정의를 컴포넌트 밖으로 빼거나, useCallback으로 감싸줘서 매 렌더링마다 새롭게 만들지 않게 할 필요가 있다.
    const handleChange = useCallback((e) => {   //e = 버튼 onClick에서의 변화를 인자로 받은 것.
        console.log('change', e.target.value);
    }, []);
    
    return (
    // 이벤트 연결하기
        <div>
            <button onClick={handleClick1}>
                Button1
            </button>
            <button onClick={handleClick2}>
                Button2
            </button>
            <div>
                <input
                type="text"
                onChange={handleChange}
                />
            </div>

        </div>
    );
}

export default App;

2. 이벤트의 종류

2.1. 마우스와 관련된 이벤트

  • onClick: 마우스 버튼을 ‘클릭’했을 때 발생 (Down + Up)
  • onMouseDown: 마우스 버튼을 ‘눌렀을 때’ 발생
  • onMouseUp: 마우스 버튼을 ‘뗐을 때’ 발생
  • onMouseEnter: 요소 밖에서 안으로 마우스가 들어갔을 때 발생
  • onMouseLeave: 요소 안에서 밖으로 마우스가 나갔을 때 발생
  • onMouseMove: 요소 안에서 마우스 커서를 움직였을 때 발생.

2.2. 키보드와 관련된 이벤트

  • onKeyDown: 키보드의 자판을 눌렀을 때 발생(물리적인 키에 반응)
  • onKeyUp: 키보드의 자판을 뗐을 때 발생
  • onKeyPress: 키보드의 자판을 눌러 ‘문자가 입력되었을 때’ 발생

2.3. Focus, Form 이벤트

  • onFocus: 요소가 Focus되었을 때 발생
  • onBlur: 요소의 Focus가 사라지면 발생
  • onChange: 요소의 값이 변화하면 발생

3. Form

  • Controlled Component: React에 의해 입력 요소값이 제어되는 컴포넌트.
  • 장점: 컴포넌트의 state와 input value가 완전히 동일한 값을 가짐(신뢰 가능한 단일 출처), 다른 컴포넌트에 input value를 전달하거나 다른 이벤트 핸들러에서 값을 재설정해줄 수 있음
  • 단점: 값이 변경될 때마다 렌더링이 됨(컴포넌트의 영향 범위가 클수록 성능 저하)
  • UnControlled Component: Controlled Component와는 반대로, input value를 직접 DOM에서 가져와 사용. 이때 렌더링은 단 한 번만 하게 됨.
// Controlled Component의 형식
function TextInput(){
	const [text, setText] = useState('');
	
	return(
		<input
			type='text'
			value={text}
			onChange={(e)=>{
				setText(e.target.value);
			}}
		/>
	);
}

3.1. Controlled vs Uncontrolled Component

//1. Controlled Component - TextInput
import {useState} from "react";

function TextInput(){
    const [value, setValue] = useState('');
    console.log('[TextInput] render', value);
    //form 요소와 value가 직접적으로 연결된 controlled component.
    //렌더링이 value가 변경될 때마다 일어나게 됨.
    return(
        <input
            type="text"
            value={value}
            onChange={(e)=>{
                setValue(e.target.value);
            }}
        />
    );
}
export default TextInput;
//2. UnControlled Component - UncontrolledTextInput
import {useRef} from "react";

function UncontrolledTextInput(){
    const inputRef = useRef();  //useRef: DOM의 특정 Reference를 직접 가져올 때 사용.
    //Uncontrolled Component: input value를 state에 연결하지 않고, 직접 useRef를 이용해 DOM 객체에 접근하여 사용.
    console.log('[UncontrolledTextInput] render');

    return(
        <>
            <input
                ref={inputRef} type="text"  //가져온 객체를 직접 연결하여 사용
            />
            <button
                onClick={()=>{
                    console.log(inputRef.current.value);
                    //state의 상태는 변화하지 않기 때문에, 값이 변경되더라도 Rerendering이 되지 않는다.
                }}
                >
                Get Value
            </button>
        </>
    );
}
export default UncontrolledTextInput;

4. 설문조사 만들기

  • 이름과 사는 곳을 입력받는 설문조사 폼 만들기
  • 만약 사는 곳이 ‘한국’인 경우, ‘2-1. 한국 어디에 사나요?’라는 항목을 노출시키기.
  • 1번과 2번을 모두 입력하지 않았다면 저장 버튼 비활성화.
  • 저장 버튼 클릭 시 “저장되었습니다”라는 문구를 띄우고, 입력된 폼 내용을 모두 제거.

4.1. 입력 코드

//1. TextInputEx.js: 이름과 한국 어디에 사는 지 입력할 때 사용됨.
function TextInputEx({value, setValue}){
    //form 요소와 value가 직접적으로 연결된 controlled component.
    //렌더링이 value가 변경될 때마다 일어나게 됨.
    return(
        <input
            type="text"
            value={value}
            onChange={(e)=>{
                setValue(e.target.value);
            }}
        />
    );
}
export default TextInputEx;
//2. SelectEx.js: 사는 곳을 선택할 때 사용.
function SelectEx({value, setValue, options= []}){
    return(
        <select value={value}
                onChange={(e)=>{
                setValue(e.target.value);
            }}
        >
            <option value="" disabled>지역을 선택해 주세요.</option>
            {options.map((item) => (
                <option key={item} value={item}>{item}</option>
                ))}
        </select>
    );
}
export default SelectEx;
//3. SSulMoon.js: 메인 페이지 렌더링.
import {useState} from "react";
import TextInputEx from "./eventComponent/TextInputEx";
import SelectEx from "./eventComponent/SelectEx";

const countryOptions = [
    "한국",
    "러시아",
    "일본",
    "중국",
    "미국"
];
function SSulMoon(){
    const [formValue, setFormValue] = useState({
        name: "",
        country: "",
        address: ""
    });
    console.log('[App] formValue', formValue);

    return(
        <div className="App">
            <div className="form">
                <div className="form-item">
                    <h1>1. 이름이 무엇인가요?</h1>
                    <TextInputEx value={formValue.name} setValue={(value)=>{
                        setFormValue((state)=>({
                            ...state,   //기존의 formValue 내부 요소들을 그대로 유지해 주는 기능.
                            name: value   //name만 바꿔준다.
                        }));
                    }}/>
                </div>
                <div className="form-item">
                    <h1>2. 사는 곳은 어디인가요?</h1>
                    <SelectEx value={formValue.country}
                              setValue={(value)=>{
                                setFormValue((state)=>({
                                    ...state,
                                    country: value
                                }));
                              }}
                              options={countryOptions}
                    />
                </div>
                {formValue.country === "한국" && ( //만약 country의 값이 '한국'이라면,
                    <div className="form-item">
                        <h1>2-1. 한국 어디에 사나요?</h1>
                        <TextInputEx value={formValue.address} setValue={(value) => {
                            setFormValue((state) => ({
                                ...state,   //기존의 formValue 내부 요소들을 그대로 유지해 주는 기능.
                                address: value   //address만 바꿔준다.
                            }));
                        }}/>
                    </div>
                )}
                {/*2-1의 div를 활성화해주고, 아니라면 null값을 출력한다.*/}

                <div className="button-group">
                    <button onClick={() => {
                        alert('저장되었습니다');
                        setFormValue({
                            name: "",
                            country: "",
                            address: ""
                        });
                    }}
                            disabled={!formValue.name || !formValue.country}
                        //name과 country가 공백이면 버튼이 비활성화 되도록 만들기.
                            >
                        저장
                    </button>
                </div>
            </div>

        </div>
    )
}

export default SSulMoon;

4.1. 결과 페이지

설문페이지.png

설문저장.png

profile
사람도 사랑도 계획적으로

0개의 댓글