사용자가 입력한 값을 추출하는 작업
사용자가 제공한 데이터를 검증하는 작업 → 만약 부정확한 입력창을 제시하면 사용자에게 검증 오류를 제시하기도 한다.
type="button" 설정event.preventDefault()로 설정하여 기본 동작을 막는다.import { useState } from "react";
export default function Login() {
// const [enteredEmail, setEnteredEmail] = useState("");
// const [enteredPW, setEnteredPW] = useState("");
const [enteredValue, setEnteredValue] = useState({
email: "",
pw: "",
});
function handleSubmit(event) {
event.preventDefault();
console.log("User Email: ", enteredValue.email);
console.log("User PW: ", enteredValue.pw);
}
function handleInputChange(identifier, value) {
setEnteredValue((prevValues) => ({
...prevValues,
[identifier]: value,
}));
}
// function handlerEmailChange(event) {
// setEnteredEmail(event.target.value);
// }
// function handlerPWChange(event) {
// setEnteredPW(event.target.value);
// }
return (
<form onSubmit={handleSubmit}>
<h2>Login</h2>
<div className="control-row">
<div className="control no-margin">
<label htmlFor="email">Email</label>
<input
id="email"
type="email"
name="email"
onChange={(event) => handleInputChange("email", event.target.value)}
value={enteredValue.email}
/>
</div>
<div className="control no-margin">
<label htmlFor="password">Password</label>
<input
id="password"
type="password"
name="password"
onChange={(event) => handleInputChange("pw", event.target.value)}
value={enteredValue.pw}
/>
</div>
</div>
<p className="form-actions">
<button className="button button-flat">Reset</button>
<button className="button">Login</button>
</p>
</form>
);
}

import { useRef } from "react";
export default function Login() {
const enteredEmail = useRef();
const enteredPw = useRef();
function handleSubmit(event) {
event.preventDefault();
const email = enteredEmail.current.value;
const pw = enteredPw.current.value;
console.log("User Email: ", email);
console.log("User PW: ", pw);
}
return (
<form onSubmit={handleSubmit}>
<h2>Login</h2>
<div className="control-row">
<div className="control no-margin">
<label htmlFor="email">Email</label>
<input id="email" type="email" name="email" ref={enteredEmail} />
</div>
<div className="control no-margin">
<label htmlFor="password">Password</label>
<input
id="password"
type="password"
name="password"
ref={enteredPw}
/>
</div>
</div>
<p className="form-actions">
<button className="button button-flat">Reset</button>
<button className="button">Login</button>
</p>
</form>
);
}
export default function Signup() {
function handleSubmit(event) {
event.preventDefault();
// form에 입력된 각기 다른 값들을 쉽게 얻을 수 있도록 도와주는 객체 => event.targe === form
// 이를 사용하기 위해선 input에 name 속성을 가져야한다.
const fd = new FormData(event.target);
const data = Object.fromEntries(fd.entries());
// FormData의 엔트리를 부르는 것은 모든 입력창과 그에 속한 값들의 배열을 제공한다.
// 그리고 그 배열에 있는 엔트리로부터 Object를 불러내면 모든 입력창과 핵심 값들을 가지고 있는 객체를 가질 수 있다.
console.log(data);
}
return (
<form onSubmit={handleSubmit}>
<h2>Welcome on board!</h2>
<p>We just need a little bit of data from you to get you started 🚀</p>
<div className="control">
<label htmlFor="email">Email</label>
<input id="email" type="email" name="email" />
</div>
<div className="control-row">
<div className="control">
<label htmlFor="password">Password</label>
<input id="password" type="password" name="password" />
</div>
<div className="control">
<label htmlFor="confirm-password">Confirm Password</label>
<input
id="confirm-password"
type="password"
name="confirm-password"
/>
</div>
</div>
<hr />
<div className="control-row">
<div className="control">
<label htmlFor="first-name">First Name</label>
<input type="text" id="first-name" name="first-name" />
</div>
<div className="control">
<label htmlFor="last-name">Last Name</label>
<input type="text" id="last-name" name="last-name" />
</div>
</div>
<div className="control">
<label htmlFor="phone">What best describes your role?</label>
<select id="role" name="role">
<option value="student">Student</option>
<option value="teacher">Teacher</option>
<option value="employee">Employee</option>
<option value="founder">Founder</option>
<option value="other">Other</option>
</select>
</div>
<fieldset>
<legend>How did you find us?</legend>
<div className="control">
<input
type="checkbox"
id="google"
name="acquisition"
value="google"
/>
<label htmlFor="google">Google</label>
</div>
<div className="control">
<input
type="checkbox"
id="friend"
name="acquisition"
value="friend"
/>
<label htmlFor="friend">Referred by friend</label>
</div>
<div className="control">
<input type="checkbox" id="other" name="acquisition" value="other" />
<label htmlFor="other">Other</label>
</div>
</fieldset>
<div className="control">
<label htmlFor="terms-and-conditions">
<input type="checkbox" id="terms-and-conditions" name="terms" />I
agree to the terms and conditions
</label>
</div>
<p className="form-actions">
<button type="reset" className="button button-flat">
Reset
</button>
<button type="submit" className="button">
Sign up
</button>
</p>
</form>
);
}

naem="acquisition" 부분이 포함되지 않았음을 알 수 있다.entries나 fromEntries를 사용할 때는 빠져있다.function handleSubmit(event) {
event.preventDefault();
const fd = new FormData(event.target);
const acquisitionChannel = fd.getAll("acquisition"); // getAll
const data = Object.fromEntries(fd.entries());
data.acquisition = acquisitionChannel;
console.log(data);
}

<button type="reset">Reset</button>
function handleSubmit(event) {
event.preventDefault();
// 버튼의 타입을 reset으로 설정하는 것과 같다.
// => 참조를 이용할 때 email.current.value='' 라기 보다는 이렇게 사용하는게 낫다.
event.target.reset();
}
export default function Login() {
const emailIsInvalid =
enteredValue.email !== "" && !enteredValue.email.includes("@");
return (
{/*...*/}
<div className="control-error">
{emailIsInvalid && <p>유효한 이메일 주소를 입력해주세요.</p>}
</div>
{/*...*/}
);
}

import { useState } from "react";
export default function Login() {
const [enteredValue, setEnteredValue] = useState({
email: "",
pw: "",
});
const [didEdit, setDidEdit] = useState({
email: false,
pw: false,
}); // 상태 추가
// didEdit 상태가 true이고 이메일 입력창에 @가 포함되지 않을때
const emailIsInvalid = didEdit.email && !enteredValue.email.includes("@");
function handleSubmit(event) {
event.preventDefault();
console.log("User Email: ", enteredValue.email);
console.log("User PW: ", enteredValue.pw);
}
function handleInputChange(identifier, value) {
setEnteredValue((prevValues) => ({
...prevValues,
[identifier]: value,
}));
setDidEdit((prevEdit) => ({
...prevEdit,
[identifier]: false,
})); // 사용자가 입력창에 입력할 때 다시 해당 요소의 didEdit을 false로 업데이트
}
function handleInputBlur(identifier) {
setDidEdit((prevEdit) => ({ ...prevEdit, [identifier]: true })); // 포커스를 이동할 때 didEdit을 true로 설정
}
return (
<form onSubmit={handleSubmit}>
<h2>Login</h2>
<div className="control-row">
<div className="control no-margin">
<label htmlFor="email">Email</label>
<input
id="email"
type="email"
name="email"
onBlur={() => handleInputBlur("email")} // blur 이벤트 동작
onChange={(event) => handleInputChange("email", event.target.value)}
value={enteredValue.email}
/>
<div className="control-error">
{emailIsInvalid && <p>유효한 이메일 주소를 입력해주세요.</p>}
</div>
</div>
<div className="control no-margin">
<label htmlFor="password">Password</label>
<input
id="password"
type="password"
name="password"
onChange={(event) => handleInputChange("pw", event.target.value)}
value={enteredValue.pw}
/>
</div>
</div>
<p className="form-actions">
<button className="button button-flat">Reset</button>
<button className="button">Login</button>
</p>
</form>
);
}

import { useState, useRef } from "react";
export default function Login() {
const [emailIsInvalid, setEmailIsInvalid] = useState(false);
const enteredEmail = useRef();
const enteredPw = useRef();
function handleSubmit(event) {
event.preventDefault();
const email = enteredEmail.current.value;
const pw = enteredPw.current.value;
const emailIsValid = email.includes("@");
if (!emailIsValid) {
setEmailIsInvalid(true);
return;
}
setEmailIsInvalid(false);
}
return (
<form onSubmit={handleSubmit}>
<h2>Login</h2>
<div className="control-row">
<div className="control no-margin">
<label htmlFor="email">Email</label>
<input id="email" type="email" name="email" ref={enteredEmail} />
<div className="control-error">
{emailIsInvalid && <p>유효한 이메일을 입력해주세요.</p>}
</div>
</div>
<div className="control no-margin">
<label htmlFor="password">Password</label>
<input
id="password"
type="password"
name="password"
ref={enteredPw}
/>
</div>
</div>
<p className="form-actions">
<button className="button button-flat">Reset</button>
<button className="button">Login</button>
</p>
</form>
);
}

required : 브라우저가 사용자 입력을 검증할 수 있도록 입력 요소에 설정해 둘 수 있는 빌트인 속성.type : email 타입에 맞는 입력을 요구함.minLength : 입력창에 최소한으로 입력되야할 글자 수import { useState } from "react";
export default function Signup() {
const [pwsAreNotEqual, setPwsAreNotEqual] = useState(false);
function handleSubmit(event) {
event.preventDefault();
const fd = new FormData(event.target);
const acquisitionChannel = fd.getAll("acquisition");
const data = Object.fromEntries(fd.entries());
data.acquisition = acquisitionChannel;
// validation
if (data.password !== data["confirm-password"]) {
setPwsAreNotEqual(true);
return;
}
console.log(data);
event.target.reset();
}
return (
<form onSubmit={handleSubmit}>
<h2>Welcome on board!</h2>
<p>We just need a little bit of data from you to get you started 🚀</p>
<div className="control">
<label htmlFor="email">Email</label>
<input id="email" type="email" name="email" required />
</div>
<div className="control-row">
<div className="control">
<label htmlFor="password">Password</label>
<input
id="password"
type="password"
name="password"
required
minLength={6}
/>
</div>
<div className="control">
<label htmlFor="confirm-password">Confirm Password</label>
<input
id="confirm-password"
type="password"
name="confirm-password"
required
/>
{/* validation */}
<div className="control-error">
{pwsAreNotEqual && <p>비밀번호가 일치하지 않습니다.</p>}
</div>
</div>
</div>
<hr />
<div className="control-row">
<div className="control">
<label htmlFor="first-name">First Name</label>
<input type="text" id="first-name" name="first-name" required />
</div>
<div className="control">
<label htmlFor="last-name">Last Name</label>
<input type="text" id="last-name" name="last-name" required />
</div>
</div>
<div className="control">
<label htmlFor="phone">What best describes your role?</label>
<select id="role" name="role" required>
<option value="student">Student</option>
<option value="teacher">Teacher</option>
<option value="employee">Employee</option>
<option value="founder">Founder</option>
<option value="other">Other</option>
</select>
</div>
<fieldset>
<legend>How did you find us?</legend>
<div className="control">
<input
type="checkbox"
id="google"
name="acquisition"
value="google"
/>
<label htmlFor="google">Google</label>
</div>
<div className="control">
<input
type="checkbox"
id="friend"
name="acquisition"
value="friend"
/>
<label htmlFor="friend">Referred by friend</label>
</div>
<div className="control">
<input type="checkbox" id="other" name="acquisition" value="other" />
<label htmlFor="other">Other</label>
</div>
</fieldset>
<div className="control">
<label htmlFor="terms-and-conditions">
<input
type="checkbox"
id="terms-and-conditions"
name="terms"
required
/>
I agree to the terms and conditions
</label>
</div>
<p className="form-actions">
<button type="reset" className="button button-flat">
Reset
</button>
<button type="submit" className="button">
Sign up
</button>
</p>
</form>
);
}

export default function Input({ label, id, error, ...props }) {
return (
<div className="control no-margin">
<label htmlFor={id}>{label}</label>
<input id={id} {...props} />
<div className="control-error">{error && <p>{error}</p>}</div>
</div>
);
}
import { useState } from "react";
import Input from "./Input";
export default function Login() {
const [enteredValue, setEnteredValue] = useState({
email: "",
pw: "",
});
const [didEdit, setDidEdit] = useState({
email: false,
pw: false,
});
const emailIsInvalid = didEdit.email && !enteredValue.email.includes("@");
const pwIsInvalid = didEdit.pw && enteredValue.pw.trim().length < 6;
function handleSubmit(event) {
event.preventDefault();
console.log("User Email: ", enteredValue.email);
console.log("User PW: ", enteredValue.pw);
}
function handleInputChange(identifier, value) {
setEnteredValue((prevValues) => ({
...prevValues,
[identifier]: value,
}));
setDidEdit((prevEdit) => ({
...prevEdit,
[identifier]: false,
}));
}
function handleInputBlur(identifier) {
setDidEdit((prevEdit) => ({ ...prevEdit, [identifier]: true }));
}
return (
<form onSubmit={handleSubmit}>
<h2>Login</h2>
<div className="control-row">
<Input
label="Email"
id="email"
type="email"
name="email"
onBlur={() => handleInputBlur("email")}
onChange={(event) => handleInputChange("email", event.target.value)}
value={enteredValue.email}
error={emailIsInvalid && "이메일 유형이 잘못되었습니다."}
/>
<Input
label="Password"
id="password"
type="password"
name="password"
onBlur={() => handleInputBlur("pw")}
onChange={(event) => handleInputChange("pw", event.target.value)}
value={enteredValue.pw}
error={pwIsInvalid && "비밀번호는 6글자 이상이어야 합니다."}
/>
</div>
<p className="form-actions">
<button className="button button-flat">Reset</button>
<button className="button">Login</button>
</p>
</form>
);
}

export function isEmail(value) {
return value.includes("@");
}
export function isNotEmpty(value) {
return value.trim() !== "";
}
export function hasMinLength(value, minLength) {
return value.length >= minLength;
}
export function isEqualsToOtherValue(value, otherValue) {
return value === otherValue;
}
import { isEmail, isNotEmpty, hasMinLength } from "../util/validation.js";
export default function Login() {
const emailIsInvalid =
didEdit.email &&
!isEmail(enteredValue.email) &&
!isNotEmpty(enteredValue.email);
const pwIsInvalid =
didEdit.pw &&
!hasMinLength(enteredValue.pw, 6) &&
!isNotEmpty(enteredValue.pw);
}
import { useState } from "react";
export default function useInput(defaultValue, validationFn) {
const [enteredValue, setEnteredValue] = useState(defaultValue);
const [didEdit, setDidEdit] = useState(false);
const valueIsValid = validationFn(enteredValue);
function handleInputChange(event) {
setEnteredValue(event.target.value);
setDidEdit(false);
}
function handleInputBlur() {
setDidEdit(true);
}
return {
value: enteredValue,
handleInputChange,
handleInputBlur,
hasError: didEdit && !valueIsValid,
};
}
import { useState } from "react";
import Input from "./Input";
import { isEmail, isNotEmpty, hasMinLength } from "../util/validation.js";
import useInput from "../hooks/useInput.js";
export default function Login() {
const {
value: emailValue,
handleInputChange: handleEmailChange,
handleInputBlur: handleEmailBlur,
hasError: emailHasError,
} = useInput("", (value) => isEmail(value) && isNotEmpty(value));
const {
value: pwValue,
handleInputChange: handlePwChange,
handleInputBlur: handlePwBlur,
hasError: pwHasError,
} = useInput("", (value) => hasMinLength(value, 6) && isNotEmpty(value));
function handleSubmit(event) {
event.preventDefault();
if (emailHasError || pwHasError) {
return;
}
console.log("User Email: ", emailValue);
console.log("User PW: ", pwValue);
}
return (
<form onSubmit={handleSubmit}>
<h2>Login</h2>
<div className="control-row">
<Input
label="Email"
id="email"
type="email"
name="email"
onBlur={handleEmailBlur}
onChange={handleEmailChange}
value={emailValue}
error={emailHasError && "이메일 유형이 잘못되었습니다."}
/>
<Input
label="Password"
id="password"
type="password"
name="password"
onBlur={handlePwBlur}
onChange={handlePwChange}
value={pwValue}
error={pwHasError && "비밀번호는 6글자 이상이어야 합니다."}
/>
</div>
<p className="form-actions">
<button className="button button-flat">Reset</button>
<button className="button">Login</button>
</p>
</form>
);
}
