- 사용자로부터 입력을 받기 위해 사용하는 것
- 텍스트 입력, 체크박스, 셀렉트 등 사용자가 선택해야 하는 모든 것
- 아래와 같은 HTML 폼도 리액트에서 잘 작동 하지만 자바스크립트 코드를 통해 사용자가 입력한 값에 접근하기는 매우 불편
<form>
<labal>
이름:
<input type="text" name="name" />
</label>
<button type="submit">제출</button>
</form>
- 리액트에서는 사용자가 입력한 값에 접근하고 제어할 수 있도록 제어 컴포넌트(Controlled Components) 사용
제어 컴포넌트
- 사용자가 입력한 값에 접근하고 제어할 수 있도록 해주는 컴포넌트
- HTML 폼은 각 엘리먼트가 자체적으로 state를 관리
<input>
, <textarea>
, <select>
등이 각각 내부에 state를 가지고 있음
- 자바스크립트 코드를 통해 각각의 값에 접근하기가 쉽지 않음
- 제어 컴포넌트에서는 모든 데이터를 state에서 관리
- state 값을 변경하기 위해서는 무조건
setState()
함수를 사용해야 함
- 함수 컴포넌트에서는
useState()
훅을 사용하여 state 관리
- 위에서 작성했던 사용자의 이름을 입력 받는 HTML 폼을 리액트의 제어 컴포넌트로 만들면 다음과 같음
- input 태그의
value={value}
에서 리액트 컴포넌트의 state
에 들어 있는 값을 input에 항상 표시
- 입력 값이 변경되면
onChange={handleChange}
에서 handleChange
함수 호출
setValue()
함수를 사용하여 새롭게 변경된 값을 value
라는 이름의 state
에 저장
event
는 이벤트 객체를 나타네며 event.target
은 현재 발생한 이벤트의 타겟, event.target.value
는 해당 타겟의 value
속성값을 의미
import React, {useState} from 'react';
function NameForm(props) {
const [value, setValue] = useState('');
const handleChange = (event) => {
setValue(event.target.value);
}
const handleSubmit = (event) => {
alert('입력한 이름: ' + value);
event.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<label>
이름:
<input type="text" value={value} onChange={handleChange} />
</label>
<button type="submit">제출</button>
</form>
)
}
export default NameForm
- 이처럼 제어 컴포넌트를 사용하면 입력값이 리액트 컴포넌트의 state를 통해 관리되고, 여러 개의 입력 양식 값을 원하는대로 조종 가능
- 예를 들어 사용자가 입력한 모든 알파벳을 대문자로 변경시켜서 관리하고 싶다면 아래 코드와 같이 작성하면 됨
const handleChange = (event) => {
setValue(event.target.value.toUpperCase());
}
textarea 태그
- 여러 줄에 걸쳐서 나올 정도의 긴 텍스트를 입력받기 위한 HTML 태그
- HTML 에서는 아래와 같이 텍스트를 태그가 감싸는 형태로 사용
<textarea>
이곳에 긴 텍스트가 들어가게 됩니다.
</textarea>
- 리액트에서는
<textarea>
태그에 value라는 attribute를 사용하여 텍스트를 표시
- 제어 컴포넌트 방식이므로 값을 컴포넌트의 state를 사용해서 다룰 수 있음
- 아래 코드는 고객으로부터 요청사항을 입력받기 위한 RequestForm 이라는 컴포넌트
- state로는 value가 있고, 이 값을
<textarea>
태그의 value라는 attribute에 넣어줌으로써 화면에 나타나게 됨
- 여기에서는 value를 선언할 때 초깃값을 넣어줬기 때문에 처음 렌더링될 때부터
<textarea>
에 텍스트가 나타남
import React, {useState} from 'react';
function RequestForm(props) {
const [value, setValue] = useState('요청사항을 입력하세요.');
const handleChange = (event) => {
setValue(event.target.value);
}
const handleSubmit = (event) => {
alert('입력한 요쳥사항: ' + value);
event.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<label>
요청사항:
<textarea value={value} onChange={handleChange} />
</label>
<button type="submit">제출</button>
</form>
)
}
export default RequestForm
select 태그
- 드롭다운(drop-down) 목록을 보여주기 위한 HTML 태그
- 여러 가지 옵션 중에서 하나를 선택할 수 있는 기능을 제공
- HTML 에서는 아래 코드와 같이
<option>
태그를 <select>
태그가 감싸는 형태로 사용
- 현재 선택된 옵션의 경우 selected라는 attribute를 가지고 있음
<select>
<option value="apple">사과</option>
<option value="banana">바나나</option>
<option selected value="grape">포도</option>
<option value="watermelon">수박</option>
</select>
- 리액트의 제어 컴포넌트로 만들면 다음과 같음
- FruitSelect라는 컴포넌트의 state로 grape라는 초깃값을 가진 value 설정
handleChange()
함수에서 setValue()
함수를 사용하여 값을 업데이트
import React, {useState} from 'react';
function FruitSelect(props) {
const [value, setValue] = useState('grape');
const handleChange = (event) => {
setValue(event.target.value);
}
const handleSubmit = (event) => {
alert('선택한 과일: ' + value);
event.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<label>
과일을 선택하세요:
<select value={value} onChange={handleChange}>
<option value="apple">사과</option>
<option value="banana">바나나</option>
<option value="grape">포도</option>
<option value="watermelon">수박</option>
</select>
</label>
<button type="submit">제출</button>
</form>
)
}
export default FruitSelect
- 만약 목록에서 다중으로 선택이 되도록 하려면 아래와 같이 multiple라는 속성 값을 true로 하고, value로 선택된 옵션의 값이 들어있는 배열을 넣어주면 됨
<select multiple={true} value={['B', 'C']}>
- 디바이스의 저장 장치로부터 사용자가 하나 또는 여러 개의 파일을 선택할 수 있게 해주는 HTML 태그
- 서버로 파일을 업로드하거나 자바스크립트의 File API를 사용해서 파일을 다룰 때 사용
- 아래와 같이
<input>
태그를 사용하고 타입을 file로 해주면 됨
- File input 태그는 그 값이 읽기 전용이기 때문에 리액트에서는 비제어 컴포넌트가 됨
<input type="file" />
여러 개의 입력 다루기
- 하나의 컴포넌트에서 여러 개의 입력을 다루기 위해서는 어떻게 해야할까?
- 여러 개의 state를 선언하여 각각의 입력에 대해 사용하면 됨
- 아침 식사 선택 유무를 checkbox type으로, 방문객 수를 number type으로 제출할 수 있는 Reservation 컴포넌트를 작성해보자.
import React, {useState} from 'react';
function Reservation(props) {
const [haveBreakfast, setHaveBreakfast] = useState(true);
const [numberOfGuest, setNumberOfGuest] = useState(2);
const handleSubmit = (event) => {
alert(`아침식사 여부: ${haveBreakfast}, 방문객 수: ${numberOfGuest}`);
event.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<label>
아침식사 여부:
<input type="checkbox" checked={haveBreakfast}
onChange={(event) => {
setHaveBreakfast(event.target.checked)
}} />
</label>
<br />
<label>
방문객 수:
<input type="number" value={numberOfGuest}
onChange={(event) => {
setNumberOfGuest(event.target.value)
}} />
</label>
<button type="submit">제출</button>
</form>
)
}
export default Reservation
- 앞에서 배운 것 처럼 제어 컴포넌트에 value prop을 정해진 값으로 넣으면 코드를 수정하지 않는 한 입력값을 바꿀 수 없다.
- 만약 value prop은 넣되 자유롭게 입력할 수 있게 만들고 싶다면 앞에 undefined 또는 null을 넣어주면 된다.
- 아래 코드를 예로 들면,
- 처음에는 input 값이 hi로 정해져있어서 값을 바꿀 수 없는 입력 불가 상태였다가 timer에 의해 1초 뒤에 value가 null인 input 태그가 렌더링되면서 입력 가능한 상태로 바뀐다.
ReactDOM.render(<input value="hi" />, rootNode);
setTime(function() {
ReactDOM.render(<input value={null} />, rootNode);
}, 1000);
- 이러한 방법을 잘 활용하면 value prop을 넣으면서 동시에 사용자가 자유롭게 입력할 수 있게 만들 수 있다.
실습 - 사용자 정보 입력받기
<input>
태그의 text
타입으로 이름을 입력 받고, <select>
태그로 성별을 입력받는 SignUp 컴포넌트를 작성해보자.
import React, { useState } from "react";
function SignUp(props) {
const [name, setName] = useState("");
const [gender, setGender] = useState("남자");
const handleChangeName = (event) => {
setName(event.target.value);
};
const handleChangeGender = (event) => {
setGender(event.target.value);
};
const handleSubmit = (event) => {
alert(`이름: ${name}, 성별: ${gender}`);
event.preventDefault();
};
return (
<form onSubmit={handleSubmit}>
<label>
이름:
<input type="text" value={name} onChange={handleChangeName} />
</label>
<br />
<label>
성별:
<select value={gender} onChange={handleChangeGender}>
<option value="남자">남자</option>
<option value="여자">여자</option>
</select>
</label>
<button type="submit">제출</button>
</form>
);
}
export default SignUp;
실습 전체 코드
References
글이 많은 도움이 되었습니다, 감사합니다.