🌟 폼(form) 태그
🔖 폼(form) 태그
- HTML 폼 엘리먼트는 폼 엘리먼트 자체가 내부 상태를 가짐
- React의 다른 DOM 엘리먼트와 다르게 동작
→ form 은 제어 컴포넌트(controlled components) 중 하나
→ 리액트 form에서는 컴포넌트 안 state 데이터가 사용자가 화면에 보고 있는 데이터와 동일하도록 만드는 것을 권장
→ submit 같은 특정 이벤트가 발생했을 때 입력받은 데이터에 접근하는 방식
→ 리액트의 form 은 입력할 때 마다 특정 부분이 리렌더링 되어도 리액트 자체적으로 효율적이게 DOM을 없데이트 해주기 때문에 성능은 크게 걱정하지 않아도 됨
- form 안에 여러가지 요소가 들어있을 땐 name 속성을 부여해 각각을 구분할 수 있도록 함
→ event.target.name 으로 조회 가능
- value 속성을 통해 값을 부여한 경우, 타이핑을 해도 업데이트 되지 않음
→ onChange, onSubmit을 통해 업데이트 함수를 지정
🌟 form 태그의 이벤트
🔖 onChange 이벤트
- input , select , textarea의 요소에서 onchange 이벤트 발생
→ input , select , textarea에서 onChange 이벤트는 요소 값 각각 모든 변경마다 발생
🔖 onSubmit 이벤트
- form 태그는 onSubmit 이벤트 발생
→ onSubmit 이벤트를 통해 submit 이벤트가 실행 되었을 때 발생
→ onChange 이벤트와 달리 요소 값 각각의 모든 변경마다 반드시 발생하지는 않음
- onChange와 onSubmit에서의 event.preventDefault는 페이지가 리로딩되는 액션을 못하게 함
🌟 제어 컴포넌트
🔖 제어 컴포넌트 (Controlled Component)
- HTML에서
<input>
,<textarea>
,<select>
와 같은 폼 엘리먼트는 일반적으로 사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트 - React에서는 변경할 수 있는 state가 컴포넌트의 state 속성에 유지되며
setState()
에 의해 업데이트 - React state를 “신뢰 가능한 단일 출처 (single source of truth)“로 만들어 두 요소를 결합
- 폼을 렌더링하는 React 컴포넌트는 폼에 발생하는 사용자 입력값을 제어
- React에 의해 값이 제어되는 입력 폼 엘리먼트를 “제어 컴포넌트“라고 함
const [상태값, 상태변경함수] = useState("")
render() {
return (
<form>
<input type="text" value={**상태값**} onChange={handleChange} />
</form>
)}
value
어트리뷰트는 폼 엘리먼트에 설정되므로 표시되는 값은 state- React state는 신뢰 가능한 단일 출처 (single source of truth)가 됨
- React state를 업데이트하기 위해 모든 키 입력에서
handleChange
가 동작하기 때문에 사용자가 입력할 때 보여지는 값이 업데이트 - 제어 컴포넌트로 사용하면, input의 값은 항상 React state에 의해 결정
🌟 input 태그, Form 태그 및 이벤트 - 예시1
🔖 onChange 이벤트
- input 태그의 onChange가 이벤트가 발생한 것을 감지하면 handleChange() 함수 실행
- handleChange() 함수는 input 값에 입력된 value 값을 출력
- input 태그 안에 있는 값은 HTML에서 관리하고 있다(리액트에서 관리 x)
/*SimpleForm.js*/
import React from "react"
export default function SimpleForm() {
const handleChange = (e) => {
console.log(e.target.value)
}
return (
<>
<label>닉네임 : </label>
<input type="text" name="nickname" onChange={handleChange}></input>
</>
)
}
🔖 onSubmit 이벤트
- form 태그를 생성해주고 form 태그에도 onSubmit 이벤트를 추가
- submit 이벤트가 발생하면 handleSunmit() 함수가 실행
- form에서 submit 된 데이터는 HTML의 submit에서 관리
→ 리액트에서 form 태그의 입력 값을 사용하기 위해서는 state가 필요
/*SimpleForm.js*/
import React from "react"
export default function SimpleForm() {
const handleChange = (e) => {
console.log(e.target.value)
}
const handleSubmit = (e) => {
console.log(e)
}
return (
<form onSubmit={handleSubmit}>
<label>닉네임 : </label>
<input type="text" name="nickname" onChange={handleChange}></input>
<input type="submit" value="제출"></input>
</form>
)
}
🔖 제어 컴포넌트 전환
- 제어 컴포넌트로 사용하기 위해 input과 연결 시켜줄 React state에 선언(nickname)
- value={}로 input 엘리먼트와 React state 연결
- 리액트의 state로 input 태그의 값을 제어할 수 있음
/*SimpleForm.js*/
import React, { useState } from "react"
export default function SimpleForm() {
const [nickname, setNickname] = useState("")
const handleChange = (e) => {
setNickname(e.target.value) //입력값 넣기
}
const handleSubmit = (e) => {
e.preventDefault() //form의 새로고침 막기
alert(nickname)
}
return (
<form onSubmit={handleSubmit}>
<label>닉네임 : </label>
<input type="text" name="nickname" onChange={handleChange} **value={nickname}**></input>
<input type="submit" value="제출"></input>
</form>
)
}
🌟 input 태그, Form 태그 및 이벤트 - 예시2
🔖 onChange 이벤트
- input 태그의 onChange가 이벤트가 발생한 것을 감지하면 handleChange() 함수 실행
- handleChange() 함수는 input 값에 입력된 value 값을 출력
- input 태그는 각 info.name과 info.phone 값을 갖는 제어 컴포넌트
import React, { useState } from 'react';
export default function Form(props) {
const [info, setInfo] = useState({
name: 'merry',
phone: '010-6666-3333'
})
const **handleChange** = (e) => {
e.preventDefault();
setInfo(info => {
const update = {
...info,
[e.target.name]: e.target.value
}
return update
})
console.log(info) //업데이트 된 info 정보 출력
}
return (
<form>
<input
type="text"
name="name"
**value={info.name}**
**onChange={handleChange}**
/>
<input
type="text"
name="phone"
**value={info.phone}**
**onChange={handleChange}**
/>
</form>
);
}
- 초기 state에는 name : merry, phone: 010-6666-3333이 들어가 들어있는 제어 컴포넌트
- input 값의 초기 값은 merry와 010-6666-3333
- input의 value를 변경할 때마다 onChange 이벤트가 발생하여 state 값을 출력해 줌
🔖 onSubmit 이벤트
- button을 클릭 했을 때, submit 이벤트가 발생
- form에 연결되어 있는 onSubmit 이벤트 핸들러 동작
- state 값을 출력해 줌
import React, { useState } from 'react';
export default function Form(props) {
const [info, setInfo] = useState({
name: 'merry',
phone: '010-6666-3333'
})
const onChange = (e) => {
e.preventDefault();
setInfo(info => {
const update = {
...info,
[e.target.name]: e.target.value
}
return update
})
console.log(info) //업데이트 된 info 정보 출력
}
const handleSubmit = (e) => {
e.preventDefault()
alert(`${info.name} ${info.phone}`)
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="name"
value={info.name}
onChange={onChange}
/>
<input
type="text"
name="phone"
value={info.phone}
onChange={onChange}
/>
<button type="submit">로그인</button>
</form>
);
}
🌟 textarea 태그
🔖 textarea
- React에서
<textarea>
는value
어트리뷰트를 대신 사용 <textarea>
를 사용하는 폼은 한 줄 입력을 사용하는 폼과 비슷하게 작성
/*SimpleForm.js*/
import React, { useState } from "react"
export default function SimpleForm() {
const [nickname, setNickname] = useState("")
const handleChange = (e) => {
setNickname(e.target.value) //입력값 넣기
}
const handleSubmit = (e) => {
e.preventDefault() //form의 새로고침 막기
alert(nickname)
}
return (
<form onSubmit={handleSubmit}>
<label>닉네임 : </label>
**<textarea value={nickname} onChange={handleChange}></textarea>**
<input type="submit" value="제출"></input>
</form>
)
}
🌟 select 태그
🔖 select
- React에서는
selected
어트리뷰트를 사용하는 대신 select
태그에value
어트리뷰트를 사용
/*SimpleForm.js*/
import React, { useState } from "react"
export default function SimpleForm() {
const [nickname, setNickname] = useState("")
const handleChange = (e) => {
setNickname(e.target.value) //입력값 넣기
}
const handleSubmit = (e) => {
e.preventDefault() //form의 새로고침 막기
alert(nickname)
}
return (
<form onSubmit={handleSubmit}>
<label>닉네임 : </label>
<select value={nickname} onChange={handleChange}>
<option value="coco">coco</option>
<option value="momo">momo</option>
<option value="dodo">dodo</option>
<option value="nono">nono</option>
</select>
<input type="submit" value="제출"></input>
</form>
)
}
🌟 다중 입력 제어
🔖 다중 입력 제어
- 여러
input
엘리먼트를 제어해야할 때, 각 엘리먼트에name
어트리뷰트를 추가 - event.target.값 을 이용해서 핸들러가 어떤 작업을 할 지 선택
🔖 두개의 입력 폼
- 두개의 입력 값 받기
- 두개의 상태 필요, 두개의 input 값 필요
- 입력 값이 두개이므로 e.target.name을 통해서 핸들러가 어떤 작업을 할지 구분
/*SimpleForm.js*/
import React, { useState } from "react"
export default function SimpleForm() {
const [nickname, setNickname] = useState("")
const [password, setPassword] = useState("")
const handleChange = (e) => {
if (e.target.name === "nickname") return setNickname(e.target.value)
return setPassword(e.target.value)
}
const handleSubmit = (e) => {
e.preventDefault() //form의 새로고침 막기
alert(`닉네임 : ${nickname}, 패스워드 : ${password}`)
}
return (
<form onSubmit={handleSubmit}>
<label>닉네임 : </label>
<input type="text" name="nickname" onChange={handleChange} value={nickname}></input>
<label>패스워드 : </label>
<input type="text" name="password" onChange={handleChange} value={password}></input>
<input type="submit" value="제출"></input>
</form>
)
}
🔖 두개의 입력 폼 - 객체로 받기
- state가 각각 있으면 복잡
→ 객체로 저장해보자
- userInput은 객체 이름
→ nickname, password가 프로퍼티
→ userInput.nickname
→ userInput.password
/*SimpleForm.js*/
import React, { useState } from "react"
export default function SimpleForm() {
**const [userInputs, setUserInputs] = useState({
nickname: "",
password: "",
})**
const handleChange = (e) => {
**setUserInputs({ ...userInputs, [e.target.name]: e.target.value })**
}
const handleSubmit = (e) => {
e.preventDefault() //form의 새로고침 막기
**const { nickname, password } = userInputs** //구조분해 할당
alert(`닉네임 : ${nickname}, 패스워드 : ${password}`)
}
return (
<form onSubmit={handleSubmit}>
<label>닉네임 : </label>
<input type="text" name="nickname" onChange={handleChange} **value={userInputs.nickname}**></input>
<label>패스워드 : </label>
<input type="text" name="password" onChange={handleChange} **value={userInputs.password}**></input>
<input type="submit" value="제출"></input>
</form>
)
}
- e.target으로 입력 받은 값은, userInputs에 추가하도록 한다.
- e.target 안에는 input으로 입력한 값이 들어있다.
- 객체 상태에 있는 값을 구조분해 할당 하여 submit 이벤트가 발생하면 alert창을 띄워주자.
🌟 비제어 컴포넌트
🔖 비제어 컴포넌트
- DOM 자체에서 폼 데이터가 다루어짐
※ 제어 컴포넌트에서 폼 데이터는 React 컴포넌트에서 다루어짐
- 비제어 컴포넌트를 만들려면 ref를 사용 하여 DOM에서 폼 값을 가져올 수 있음
- DOM에 신뢰 가능한 출처를 유지함
- 비제어 컴포넌트를 사용할 때 React와 non-React 코드를 통합하는 것이 쉬움
🌟 ref
🔖 ref
→ 리액트에서 DOM에 직접 접근
- 클래스 컴포넌트 일 때, ref 사용
import {createRef} from "react"
- 함수 컴포넌트 일 때, ref를 사용
- useRef는 Hook 함수
import {useRef} from "react"
- useRef를 통해서 객체를 만들 수 있음
→ current라는 키가 자동으로 들어있음
→ ref 태그를 태그에 가져다 붙여주면, current는 input 태그를 가지고 있음
/*SimpleForm.js*/
import React, { useRef } from "react"
//ref를 활용 -> 비제어 컴포넌트 방식으로 form을 다뤄보자
export default function UnControlledForm() {
const inputRef = useRef()
const handleChange = (e) => {
console.log(e.target.value)
}
return (
<>
<label>닉네임 : </label>
<input type="text" name="nickname" onChange={handleChange} ref={inputRef}></input>
</>
)
}
- DOM에 입력한 값을 가져올 수 있음
- useRef() 선언
→ input 태그에 ref = {inputRef} 추가
→ 리액트와 DOM이 연결되어 입력 값을 가져 올 수 있음
/*SimpleForm.js*/
import React, { useRef } from "react"
//ref를 활용 -> 비제어 컴포넌트 방식으로 form을 다뤄보자
export default function UnControlledForm() {
const inputRef = useRef()
const handleSubmit = (e) => {
e.preventDefault()
alert(inputRef.current.value)
}
return (
<form onSubmit={handleSubmit}>
<label>닉네임 : </label>
<input type="text" name="nickname" ref={inputRef}></input>
<input type="submit" value="제출"></input>
</form>
)
}
🌟 form 태그 활용 - ToDo List
🔖 기본 초기화 코드
import React, { useState } from "react";
const ToDo = () => (
<tr>
<td>
<label>123</label>
</td>
<td>
<input />
</td>
<td>
<label>밥먹기</label>
</td>
</tr>
)
export default function MyTodoList() {
const addToStart = () => {
}
const addToEnd = () => {
}
const sortByEarliest = () => {
}
const sortByLatest = () => {
}
return (
<div>
<code>Todo List</code>
<br />
<button onClick={addToStart}>앞에 추가</button>
<button onClick={addToEnd}>뒤에 추가</button>
<button onClick={sortByEarliest}>오름차순 정렬</button>
<button onClick={sortByLatest}>내림차순 정렬</button>
<table>
<tr>
<th>ID</th>
<th />
<th>created at</th>
</tr>
<ToDo />
</table>
</div>
)
}
🔖 state에 있는 값을 테이블 데이터를 만들자
- 데이터가 저장될 state 생성
→ todoCounter는 id값
→ createAt는 생성 날짜
- state에 있는 값을 하나씩 꺼내서 테이블 데이터로 ToDo리스트 생성
- 시작 값은 1이고, Date로 현재 시간을 받아왔으니까 초기 값은 아래와 같다.
import React, { useState } from "react";
const ToDo = (props) => (
<tr>
<td>
<label>{props.id}</label>
</td>
<td>
<input />
</td>
<td>
<label>{props.createdAt.toTimeString()}</label>
</td>
</tr>
);
export default function MyTodoList() {
const date = new Date()
const todoCounter = 1
const [state, setState] = useState({
todoCounter: todoCounter,
list: [
{
id: todoCounter, //id는 생성 번호
createdAt: date, //생성 날짜 / 시간
}
]
})
const addToStart = () => {
}
const addToEnd = () => {
}
const sortByEarliest = () => {
}
const sortByLatest = () => {
}
return (
<div>
<code>Todo List</code>
<br />
<button onClick={addToStart}>앞에 추가</button>
<button onClick={addToEnd}>뒤에 추가</button>
<button onClick={sortByEarliest}>오름차순 정렬</button>
<button onClick={sortByLatest}>내림차순 정렬</button>
<table>
<tr>
<th>ID</th>
<th />
<th>created at</th>
</tr>
{state.list.map((todo) => (
<ToDo key={todo.id} {...todo} />
))}
</table>
</div>
)
}
🔖 addToStart() 함수를 통해서 뒤쪽에 리스트를 생성하자
- addToStart() 함수
→ 새로운 Date 객체를 만들어서 현재 날짜와 시간을 생성
→ 기존에 있는 todoCounter에 +1 해서 숫자 증가
→ 생성한 새로운 값을 newList에 저장(…을 이용해서 기존 값을 복사하고, 새롭게 복사된 리스트에 추가하자)
→ setState()를 통해서 state에 저장
- addToStart() 함수가 동작하면서 뒤쪽으로 순차적으로 ToDo가 생성
🔖 addToEnd() 함수를 통해서 앞쪽에 리스트를 생성하자
- addToEnd() 함수
→ newList의 순서를 변경하면, 앞쪽으로 생성된다.
🔖 sortByEarliest() 함수를 통해서 오름차순 정렬하자
- sortByEarliest() 함수
→ createAt를 이용해서 정렬
→ 새롭게 정렬된 객체 데이터를 setState를 통해서 저장
- 오름차순 정렬된 순서로 다시 렌더링
🔖 sortByLatest() 함수를 통해서 내림차순 정렬하자
- sortByLatest() 함수
→ createAt를 이용해서 정렬
→ 새롭게 정렬된 객체 데이터를 setState를 통해서 저장
🔖 전체 코드
import React, { useState } from "react";
const ToDo = (props) => (
<tr>
<td>
<label>{props.id}</label>
</td>
<td>
<input />
</td>
<td>
<label>{props.createdAt.toTimeString()}</label>
</td>
</tr>
);
export default function MyTodoList() {
const date = new Date()
const todoCounter = 1
const [state, setState] = useState({
todoCounter: todoCounter,
list: [
{
id: todoCounter, //id는 생성 번호
createdAt: date, //생성 날짜 / 시간
}
]
})
const addToStart = () => {
const date = new Date()
const nextId = state.todoCounter + 1
const newList = [...state.list, { id: nextId, createdAt: date }]
setState({
list: newList,
todoCounter: nextId,
});
}
const addToEnd = () => {
const date = new Date()
const nextId = state.todoCounter + 1
const newList = [{ id: nextId, createdAt: date }, ...state.list]
setState({
list: newList,
todoCounter: nextId,
});
}
const sortByEarliest = () => {
const sortedList = state.list.sort((a, b) => {
return a.createdAt - b.createdAt
})
setState({
list: [...sortedList]
})
}
const sortByLatest = () => {
const sortedList = state.list.sort((a, b) => {
return b.createdAt - a.createdAt
})
setState({
list: [...sortedList]
})
}
return (
<div>
<code>Todo List</code>
<br />
<button onClick={addToStart}>앞에 추가</button>
<button onClick={addToEnd}>뒤에 추가</button>
<button onClick={sortByEarliest}>오름차순 정렬</button>
<button onClick={sortByLatest}>내림차순 정렬</button>
<table>
<tr>
<th>ID</th>
<th />
<th>created at</th>
</tr>
{state.list.map((todo) => (
<ToDo key={todo.id} {...todo} />
))}
</table>
</div>
)
}
'웹 개발 > React' 카테고리의 다른 글
[React] ref와 useRef는 어떻게 사용하나 (2) | 2023.06.11 |
---|---|
[React] Ref와 DOM (0) | 2022.06.20 |
[React] 이벤트 처리하기 (0) | 2022.06.20 |
[React] 컴포넌트 반복하기 (0) | 2022.06.20 |
[React] 컴포넌트 활용연습 : random-recommend (0) | 2022.06.20 |