GDSC FE-Toy Project Study Plan
• 2차 프로젝트 주제 선정 완료
• React 책 기반 개념 공부 진행
Week 5) ~11/18
• 발표자
이벤트 핸들링: 김채림
ref: DOM에 이름 달기: 김중현
📖 이벤트 핸들링
사용자가 웹 브라우저에서 DOM 요소들과 상호 작용하는 것을 이벤트라고 한다.
4.1. 리액트의 이벤트 시스템
리액트의 이벤트 시스템은 웹 브라우저의 HTML 이벤트와 인터페이스가 동일하다.
4.1.1. 이벤트를 사용할 때 주의사항
- 이벤트 이름은 카멜 표기법으로 작성.
-ex)
onclick
-> onClick,
onkeyup
->onKeyUp
2. 이벤트에 실행할 자바스크립트 코드를 전달하는 것이 아니라, 함수 형태의 값을 전달.
-화살표 함수 문법을 사용하거나 렌더링 부분 외부에 미리 만들어서 전달.
3. DOM 요소에만 이벤트를 설정할 수 있다.
-div, button, input, form, span등의 DOM 요소에는 이벤트를 설정할 수 있지만, 직접 만든 컴포넌트에는 이벤트를 자체적으로 설정 불가.
ex) <MyComponent onClick={doSomething}/>
이와 같이 MyComponent에 onClick값을 설정하면, onClick인 props를 MyComponent에 전달해줄 뿐.
4.1.2. 이벤트 종류
리액트에서 지원하는 이벤트는 다음을 참고하길 바란다.
이벤트 리액트 매뉴얼 참고
4.2 예제로 이벤트 핸들링 익히기
컴포넌트 생성 및 불러오기-> onChange 이벤트 핸들링 하기-> 임의 메서드 만들기-> input 여러개 다루기-> onKeyPress 이벤트 핸들링하기
의 순서로 공부해보겠습니다!
4.2.1. 컴포넌트 생성 및 불러오기
✍ example
//EventPractice.js
import { Component } from "react";
class EventPractice extends Component{
render(){
return(
<div>
<h1>이벤트 연습</h1>
</div>
);
}
}
export default EventPractice;
//App.js
import EventPractice from './EventPractice';
const App=()=>{
return <EventPractice/>;
};
export default App;
✔ 결과 확인
4.2.2. onChange 이벤트 핸들링하기
✍ example
import { Component } from "react";
class EventPractice extends Component{
render(){
return(
<div>
<h1>이벤트 연습</h1>
<input
type="text"
name="message"
placeholder="아무거나 입력해 보세요"
onChange={
(e)=>{
console.log(e)
}
}
/>
</div>
);
}
}
export default EventPractice;
- EventPractice 컴포넌트에 input 요소를 렌더링 하는 코드와 onChange 이벤트 설정.
✔ 결과 확인
- 이벤트 객체가 콘솔에 나타남.
✍ example
onChange={
(e)=>{
console.log(e);
}
}
- 콘솔에 기록되는 e 객체는
SyntheticEvent
로 웹 브라우저의 네이티브 이벤트를 감싸는 객체. SyntheticEvent
는 네이티브 이벤트와 달리 이벤트가 끝나면 이벤트가 초기화.- 따라서 비동기적으로 이벤트 객체를 참조할 일이 있다면
e.persist()
함수를 호출해야 함.
🧐 만약 값이 변할 때마다 인풋 값을 콘솔에 기록하고 싶다면?
✍ example
onChange={
(e)=>{
console.log(e.target.value);
}
}
e.target.value
를 이용.
✔ 결과 확인
4.2.2.2. state에 input 값 담기
✍ example
import { Component } from "react";
class EventPractice extends Component{
state={
message:''
}
render(){
return(
<div>
<h1>이벤트 연습</h1>
<input
type="text"
name="message"
placeholder="아무거나 입력해 보세요"
value={this.state.message}
onChange={
(e)=>{
this.setState({
message:e.target.value
})
}
}
/>
</div>
);
}
}
export default EventPractice;
state={message:''}
state을 constructor에서 꺼내서 초깃값을 설정.this.setState
를 호출하여 state 업데이트.value={this.state.message}
input의 value값을 state에 있는 값으로 설정.
4.2.2.3. 버튼을 누를 때 comment 값을 공백으로 설정
✍ example
import { Component } from "react";
class EventPractice extends Component{
state={
message:''
}
render(){
return(
<div>
<h1>이벤트 연습</h1>
<input
type="text"
name="message"
placeholder="아무거나 입력해 보세요"
value={this.state.message}
onChange={
(e)=>{
this.setState({
message:e.target.value
})
}
}
/>
<button onClick={
()=>{
alert(this.state.message);
this.setState({
message:''
});
}
}>확인</button>
</div>
);
}
}
export default EventPractice;
- button 태그에서
alert
을 이용하여 현재 state값을 경고창으로 보여줌. - comment값을 보여준 다음 값을 공백으로 설정.
✔ 결과 확인
4.2.3. 임의 메서드 만들기
*이벤트에 실행할 자바스크립트 코드를 전달하는 것이 아니라, 함수 형태의 값을 전달한다. *
- 이벤트를 처리할 때 렌더링을 하는 동시에 함수를 만들어서 전달하기.
2. 함수를 미리 준비하여 전달하는 방법 ✅
4.2.3.1. 기본 방식
onChange와 onClick에 전달한 함수를 따로 빼내서 컴포넌트 임의 메서드를 만들어 보자.
✍ example
import { Component } from "react";
class EventPractice extends Component{
state={
message:''
}
constructor(props)
{
super(props);
this.handleChange=this.handleChange.bind(this);
this.handleClick=this.handleClick.bind(this);
}
handleChange(e){
this.setState({
message:e.target.value
});
}
handleClick(){
alert(this.state.message);
this.setState({
message:''
});
}
render(){
return(
<div>
<h1>이벤트 연습</h1>
<input
type="text"
name="message"
placeholder="아무거나 입력해 보세요"
value={this.state.message}
onChange={this.handleChange}
/>
<button onClick={this.handleClick}>확인</button>
</div>
);
}
}
export default EventPractice;
- 임의 메서드가 이벤트로 등록되어도 this를 컴포넌트 자신으로 제대로 가리키기 위해서는 메서드를 this와 바인딩하는 작업이 필요. (
this.handleChange.bind(this)
)
❗ 만약 바인딩하지 않는다면 this가 undefined를 가리키게 됨.
4.2.3.2. Property Initializer Syntax를 사용한 메서드 작성.
메서드 바인딩은 생성자 메서드에서 하는 것이 정석이다. 그러나 새 메서드를 만들 때마다 constructor도 수정해야하므로 불편하다. 이를 더 간단하게 표현하는 방법을 알아보자!
✍ example
import { Component } from "react";
class EventPractice extends Component{
state={
message:''
}
handleChange=(e)=>{
this.setState({
message:e.target.value
});
}
handleClick=()=>{
alert(this.state.message);
this.setState({
message:''
});
}
render(){
return(
<div>
<h1>이벤트 연습</h1>
<input
type="text"
name="message"
placeholder="아무거나 입력해 보세요"
value={this.state.message}
onChange={this.handleChange}
/>
<button onClick={this.handleClick }>확인</button>
</div>
);
}
}
export default EventPractice;
- 바벨의 transform-class-properties 문법을 사용하여 작성한 코드.
- 화살표 함수 형태로 메서드를 정의하여 간단하게 표현 가능.
4.2.4. input 여러 개 다루기
event 객체를 활용해서 input을 여러 개 다뤄보자. (input 여러 개를 처리하기 위해서 메서드를 여러개 만들 필요가 없다! )
✍ example
import { Component } from "react";
class EventPractice extends Component{
state={
message:'',
username:''
}
handleChange=(e)=>{
this.setState({
[e.target.name]:e.target.value
});
}
handleClick=()=>{
alert(this.state.username+': '+this.state.message);
this.setState({
username:'',
message:''
});
}
render(){
return(
<div>
<h1>이벤트 연습</h1>
<input
type="text"
name="username"
placeholder="이름을 입력하세요"
value={this.state.username}
onChange={this.handleChange}
/>
<input
type="text"
name="message"
placeholder="아무거나 입력해보세요"
value={this.state.message}
onChange={this.handleChange}
/>
<button onClick={this.handleClick }>확인</button>
</div>
);
}
}
export default EventPractice;
e.target.name
을 사용해서 각각의 input에 맞는 값으로 state에 삽입. (각각의 input마다 name이 다르므로 ! )- 객체 안에서 key를 [ ]로 감싸면 그 안에 넣은 레퍼런스가 가리키는 실제 값이 key 값으로 사용됨.
ex)[e.target.name]:e.target.value
에서 [e.target.name]은 곧"username"이나 "message"
✔ 결과 확인
4.2.5. onKeyPress 이벤트 핸들링
✍ example
handleKeyPress=(e)=>{
if(e.key==='Enter'){
this.handleClick();
}
}
render(){
return(
<div>
<h1>이벤트 연습</h1>
<input
type="text"
name="username"
placeholder="이름을 입력하세요"
value={this.state.username}
onChange={this.handleChange}
/>
<input
type="text"
name="message"
placeholder="아무거나 입력해보세요"
value={this.state.message}
onChange={this.handleChange}
onKeyPress={this.handleKeyPress}
/>
<button onClick={this.handleClick }>확인</button>
</div>
);
}
- Enter키를 눌렀을 때 handleClick메서드를 호출할 수 있게끔
handleKeyPress 메서드
추가.
4.3 함수 컴포넌트로 구현해보기
useState을 통해 사용하는 상태에 문자열이 아닌 객체를 넣어보자.
✍ example
import { useState } from "react";
const EventPractice=()=>{
const[form, setForm]=useState({
username:'',
message:''
});
const {username, message}=form;
const onChange=e=>{
const nextForm={
...form,//기존의 form 내용을 이 자리에 복사
[e.target.name]:e.target.value// 덮어쓰기
};
setForm(nextForm);
};
const onClick=()=>{
alert(username+': '+message);
setForm({
username:'',
message:''
});
};
const onKeyPress=e=>{
if(e.key==='Enter'){
onClick();
}
};
return(
<div>
<h1>이벤트 연습</h1>
<input
type='text'
name='username'
placeholder='사용자명'
value={username}
onChange={onChange}
/>
<input
type="text"
name="message"
placeholder="아무거나 입력"
value={message}
onChange={onChange}
onKeyPress={onKeyPress}
/>
<button onClick={onClick}>확인</button>
</div>
);
};
export default EventPractice;
const[form, setForm]=useState({ });
에서 form은 객체의 현 상태, setForm은 상태를 바꿔주는 함수.기존의 form 내용을 복사하고 원하는 값으로 덮어 씌우는 작업 필요
- 여기서 사용한
... 문법
은 spread 문법. 이는 객체의 내용을 모두 "펼쳐서" 기존 객체를 복사. - 이러한 작업을, "불변성을 지킨다" 라고 함. 불변성을 지켜주어야만 리액트 컴포넌트에서 상태가 업데이트가 됐음을 감지 할 수 있고 이에 따라 필요한 리렌더링이 진행.
- ✅ 리액트에서 객체를 업데이트하게 될 때에는 기존 객체를 직접 수정하면 안되고, 새로운 객체를 만들어서, 새 객체에 변화를 주어야 한다!! **
더 자세한 설명은 링크를 참고하세요.HTML에서 id를 사용하여 DOM에 이름을 다는 것 처럼 리액트 프로젝트 내부에서 DOM에 이름을 다는 방법이 있다. 바로 ref(reference의 줄임말) 개념이다.
- 여기서 사용한
📖 ref: DOM에 이름 달기
❓ 리액트 컴포넌트 안에서 id는 사용하면 안되나?
-id를 사용할 수 있지만 특수한 경우가 아니면 사용을 권장하지는 않는다. 같은 컴포넌트를 여러 번 사용한다면 중복 id를 가진 DOM이 여러개 생기므로 잘못된 사용법이다. (HTML에서 DOM의 id는 유일해야하므로 ! )
✅ 그러나 ref는 전역적으로 작동하지 않고 컴포넌트 내부에서만 작동하기 때문에 문제가 발생하지 않는다 !
5.1. ref는 어떤 상황에서 사용해야 할까?
ref는 DOM을 꼭 직접적으로 건드려야 할 때 사용된다.
ValidationSample 컴포넌트 만들기->input에 ref 달기-> 버튼을 누를 때마다 input에 포커스 주기
순서로 실습을 진행해보자
5.1.1. 예제 컴포넌트 생성
ValidationSample.css와 ValidationSample.js 파일 생성
✍ example
//css
.success {
background-color: lightgreen;
}
.faliure {
background-color: lightcoral;
}
//js
import { Component } from 'react';
import './ValidationSample.css';
class ValidationSample extends Component{
state={
password:'',
clicked:false,
validated:false
}
handleChange=(e)=>{
this.setState({
password:e.target.value
});
}
handleButtonClick=()=>{
this.setState({
clicked:true,
validated:this.state.password==='0000'
});
}
render(){
return(
<div>
<input
type="password"
value={this.state.password}
onChange={this.handleChange}
className={this.state.clicked ? (this.state.validated ? 'success':'faliure'):''}
/>
<button onClick={this.handleButtonClick}>검증하기</button>
</div>
);
}
}
export default ValidationSample;
- input에서는 onChange가 발생하면 handleChange를 호출하여 state의 password값을 업데이트.
- button에서는 onClick 이벤트가 발생하면 handleButtonClick을 호출하여 clicked값을 true로 설정.
- input의 clssName 값은 버튼을 누르기 전에는 비어있는 문자열을 전달, 버튼을 누를 후에서는 success 또는 faliure값 전달. (이에 따라 input 색이 변함. )
5.1.2. App 컴포넌트에서 예제 컴포넌트 렌더링
✍ example
//App.js
import { Component } from "react";
import ValidationSample from './ValidationSample';
class App extends Component{
render() {
return (
<ValidationSample/>
);
}
}
export default App;
5.1.3. DOM을 꼭 사용해야 하는 상황
- 특정 input에 포커스 주기
- 스크롤 박스 조작하기
- Canvas 요소에 그림그리기
5.2. ref 사용
콜백 함수를 통한 ref 설정 | createRef를 통한 ref 설정 |
---|---|
-가장 기본적인 방법 -ref를 달고자 하는 요소에 ref라는 콜백함수를 props로 전달 | -더 적은 코드로 쉽게 사용 가능 -리액트 v16.3부터 도입 (이전 버전에서는 작동하지 않음.) |
5.2.1. 콜백 함수를 통한 ref 설정
✍ example
<input ref={(ref)=>{this.input=ref}}/>
- 콜백 함수는 ref 값을 파라미터로 전달 받음.
this.input
은 input 요소의 DOM을 가리킴. 이를 통해 input이라는 이름의 ref 생성.- ref 이름은 자유롭게 설정 가능. ex)
this.superman=ref
5.2.2. createRef를 통한 ref 설정
✍ example
import React, { Component } from "react";
class RefSample extends Component{
input=React.createRef();
handleFocus=()=>{
this.input.current.focus();
}
render(){
return(
<div>
<input ref={this.input}/>
</div>
);
}
}
export default RefSample;
- 리액트에 내장되어있는
createRef 함수
를 이용하여 ref 생성.input=React.createRef();
: 컴포넌트 내부에서 멤버 변수로React.createRef()
를 담아줌.input ref={this.input}
: ref를 달고자 하는 요소에 ref props로 넣어주면 ref 설정이 완료.this.input.current.focus();
: ref를 설정한 DOM에 접근하기 위해서는 this.input.current를 조회하면 됨.
5.2.3. 적용
버튼을 눌렀을 때, 포커스가 다시 input 쪽으로 자동으로 넘어가도록 코드를 작성해보자.
5.2.3.1. input에 ref 달기
✍ example
<input
ref={(ref)=>this.input=ref}
(...)
/>
- 콜백 함수를 이용하여 ref를 달아줌.
5.2.3.2. 버튼 onClick 이벤트 코드 수정
✍ example
handleButtonClick=()=>{
this.setState({
clicked:true,
validated:this.state.password==='0000'
});
this.input.focus();
}
- this.input이 컴포넌트 내부의 input요소를 가리킴.
- 따라서 일반 DOM을 다루듯이 코드 작성 가능.
5.3. 컴포넌트에 ref 달기
컴포넌트 내부에 있는 DOM을 컴포넌트 외부에서 사용할 때 컴포넌트에 ref를 단다.
5.3.1. 사용법
✍ example
<MyComponent
ref={(ref)=>{this.myComponent=ref}}
/>
- 컴포넌트에 ref를 달면 MyComponent 내부의 메서드 및 멤버 변수에도 접근 가능.
ex) myComponent.handleClick, myComponent.input
5.3.2. 컴포넌트 초기 설정
✍ example
//ScrollBox.js
import { Component } from "react";
class ScrollBox extends Component{
render(){
const style={
border: '1px solid black',
height: '300px',
width:'300px',
overflow:'auto',
position:'relative'
};
const innerStyle={
width:'100%',
height:'650px',
background:'linear-gradient(white, black)'
}
return(
<div style={style}
ref={(ref)=>{this.box=ref}}>
<div style={innerStyle}/>
</div>
);
}
}
export default ScrollBox;
- ScrollBox.js 컴포넌트 파일 생성. (최상위 DOM에 ref를 달아줌.)
✍ example
//App.js
import { Component } from "react";
import ScrollBox from "./ScrollBox";
class App extends Component{
render() {
return (
<ScrollBox/>
);
}
}
export default App;
- ScrollBox 렌더링.
5.3.3. 컴포넌트에 메서드 생성
✍ example
scrollToBottom=()=>{
const{scrollHeight, clientHeight}=this.box;
this.box.scrollTop=scrollHeight-clientHeight;
}
- scrollTop : 세로 스크롤바 위치
- scrollHeight : 스크롤이 있는 박스 안의 div 높이
- clientHeight : 스크롤이 있는 박스의 높이
- 스크롤바를 맨 아래쪽으로 내리려면 scrollHeight에서 clientHeight 높이를 빼면 됨.
5.3.4. 컴포넌트 ref 달고 내부 메서드 사용
✍ example
import { Component } from "react";
import ScrollBox from "./ScrollBox";
class App extends Component{
render() {
return (
<div>
<ScrollBox ref={(ref)=> this.scrollBox=ref}/>
<button onClick={()=>this.scrollBox.scrollToBottom()}>
맨 밑으로
</button>
</div>
);
}
}
export default App;
🛑주의할 점
문법상으로는 onClick={this.scrollBox.scrollBottom}
으로 써도 틀린 것은 아님. 그러나 컴포넌트가 처음 렌더링 될 때에는 this.scrollBox가 undefined이므로 this.scrollBox.scrollBottom값을 읽어오는 과정에서 오류 발생. 따라서 새로운 함수를 만들고 내부에서 this.scrollBox.scrollToBottom 메서드를 실행하면 오류가 발생하지 않음.
✔ 결과 확인
'3-1기 스터디 > Front-End 토이 프로젝트' 카테고리의 다른 글
[7주차] immer를 사용해 쉽게 불변성 유지하기 (0) | 2022.01.07 |
---|---|
[6주차] 컴포넌트 반복, 라이프사이클 메서드, 함수형 컴포넌트 (0) | 2021.12.22 |
[4주차] JSX, Component (0) | 2021.11.22 |
[3주차] 바닐라 JS로 크롬 앱 만들기 (0) | 2021.11.12 |
[2주차] 바닐라 JS로 크롬 앱 만들기 (0) | 2021.10.16 |
댓글