import { useState } from "react";
const EventPractice = () => {
const [username, setUsername] = useState("");
const [message, setMessage] = useState("");
const onChangeUsername = (e) => setUsername(e.target.value);
const onChangeMessage = (e) => setMessage(e.target.value);
const onClick = () => {
alert(username + ": " + message);
setUsername("");
setMessage("");
};
const onKeyPress = (e) => {
if (e.key === "Enter") {
onClick();
}
};
return (
<div>
<h1>이벤트 연습</h1>
<input
type="text"
name="username"
placeholder="사용자명"
value={username}
onChange={onChangeUsername}
/>
<input
type="text"
name="message"
placeholder="아무거나 입력해보세요"
value={message}
onChange={onChangeMessage}
onKeyDown={onKeyPress}
/>
<button onClick={onClick}>확인</button>
</div>
);
};
export default EventPractice;
위 코드에서는 e.target.name을 활용하지 않고, onChange 관련 함수 두 개를 따로 만들어 사용해 보았습니다.
인풋이 여러개가 아니라면 이런 코드도 좋지만 인풋의 개수가 많다면 e.target.name을 활용하는 것이 더 좋습니다.
다음은 useState 부분에서
const [username, setUsername] = useState("");
const [message, setMessage] = useState("");
문자열이 아닌 객체를 넣어 보겠습니다.
const [form, setForm] = useState({
username: "",
message: "",
});
전체 코드는 다음과 같습니다.
import { useState } from "react";
const EventPractice = () => {
const [form, setForm] = useState({
username: "",
message: "",
});
const { username, message } = form;
const onChange = (e) => {
const nextForm = {
...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}
onKeyDown={onKeyPress}
/>
<button onClick={onClick}>확인</button>
</div>
);
};
export default EventPractice;
e.target.name 값을 잘 활용하려면, 위와 같이 useState를 쓸 때 인풋 값들이 들어 있는 form 객체를 사용해 주면 됩니다.
ex ) <div id = “my-element”></div>
이렇게 특정 DOM 요소에 id를 달면 CSS에서 특정 id에 스타일을 적용하거나 자바스크립트에서 해당 id를 가진 요소를 찾아서 작업이 가능합니다.
이렇게 HTML에서 id를 사용하여 DOM에 이름을 다는 것처럼 리액트 프로젝트 내부에서 DOM에 이름을 다는 방법이 있습니다.
바로 ref(reference) 개념입니다.
일단 특정 DOM에 작업을 해야 할 때 ref를 사용한다는 것은 이미 파악했습니다. 하지만 대체 ‘어떤’ 작업을 할 때 ref를 사용해야 할 까요?
정답은 ‘DOM을 직접적으로 건드려야 할 때’ 입니다.
그렇다면 리액트 컴포넌트에서 state를 사용하여 제시한 기능을 한번 구현해 보겠습니다.
우선 클래스형 컴포넌트에서 ref를 사용하는 방법을 알아봅시다.
함수 컴포넌트에서는 ref를 사용하려면 Hooks를 사용해야 하기 때문에 Hooks를 배우면서 알아봅시다.
다음은 css 파일에 초록색과 빨간색으로 넣어두고, 인풋값에 0000이 들어가 있을 경우 버튼 클릭시 초록색 0000이 아닐경우 빨간색으로 나오도록 하는 코드를 짜봅시다.
import { Component } from "react";
import "./ValidationnSample.css";
class ValidationSample extends Component {
state = {
password: "",
clicked: false,
validated: false,
};
handleChange = (e) => {
this.setState({
password: e.target.value,
});
};
handleButtonClick = (e) => {
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" //css 초록색
: "failure" //css 빨간색
: ""
}
/>
<button onClick={this.handleButtonClick}>검증하기</button>
</div>
);
}
}
export default ValidationSample;
input 에서는 onChange 이벤트가 발생하면 handleChange를 호출하여 state 의 password 값이 업데이트 되도록 만들었고,
button 에서는 onClick 이벤트가 발생하면 handleButtonClick을 호출하여 clicked 값을 참으로 설정하고 validated 값을 검증 결과로 하였습니다.
버튼을 누르기 전까지는 값 ‘’ 만 넘겨주고 버튼 클릭 시 값에 따라서 success 값 또는 failure 값을 설정하여 색을 적용합니다.
위는 state를 사용하여 우리에게 필요한 기능을 구현했지만, 가끔 state 만으로 해결할 수 없는 기능이 있습니다.
ref 만드는 가장 기본적인 방법은 콜백 함수를 사용하는 것입니다.
ref를 달고자 하는 요소에 ref 라는 콜백 함수를 props로 전달해 주면 됩니다.
이 콜백 함수는 ref 값을 파라미터로 전달받습니다. 그리고 함수 내부에서 파라미터로 받은 ref를 컴포넌트의 멤버 변수로 설정해 줍니다.
<input ref={(ref) ⇒ { this.input = ref }} />
이렇게 하면 앞으로 this.input은 input 요소의 DOM을 가리키고 ref의 이름은 원하는 이름으로 자유롭게 지정할 수 있습니다.
DOM 타입과 관계없이 this.superman = ref 처럼 가능합니다.
ref를 만드는 또 다른 방법은 리액트에 내장되어 있는 createRef() 함수를 사용하는 것입니다
createRef를 사용하여 ref를 만들려면 우선 컴포넌트 내부에서 멤버 변수로 React.createRef()
를 담아 주어야 합니다. 그리고 해당 멤버 변수를 ref를 달고자 하는 요소에 ref props로 넣어주면
ref 설정이 완료됩니다.
조회하는 방법으로는 this.input.current를 조회하면 됩니다.
이제 input에 ref를 달아봅시다.
<input
ref={(ref) => (this.input = ref)}
리액트에서는 컴포넌트에도 ref를 달 수 있습니다.
이 방법은 주로 컴포넌트 내부에 있는 DOM을 컴포넌트 외부에서 사용할 때 씁니다.
<MyComponent ref={(ref) ⇒ { this.myComponent=ref}}/>
이렇게 하면 MyComponent 내부의 메서드 및 멤버 변수에도 접근이 가능합니다.
예제로 스크롤 박스가 있는 컴포넌트를 만들고, 스크롤바를 아래로 내리는 작업을 부모 컴포넌트에서 실행을 해보는 예제를 만들어봅시다.
순서로는
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;
다음은 컴포넌트에 스크롤바를 맨 아래쪽으로 내리는 메서드를 만들겠습니다.
스크롤바를 맨 아래쪽으로 내리려면 scrollHeight에서 clientHeight를 빼면 됩니다.
scrollToBottom = () => {
const { scrollHeight, clientHeight } = this.box;
/* 앞 코드에는 비구조화 할당 문법을 사용했습니다.
다음 코드는 같은 의미입니다,
const scrollHeight = this.box.scrollHeight;
const clientHeight = this.box.clientHeight;
*/
this.box.scrollTop = scrollHeight - clientHeight;
};
이제 App.js 에서 ref를 넣어봅시다
<div>
<ScrollBox ref={(ref) => (this.scrollBox = ref)} />
<button onClick={() => this.scrollBox.scrollToBottom()}>맨 밑으로</button>
</div>