728x90
반응형
목차
Form 제출 다루기
아래와 같이 코드를 짜면 양식 요소에서 서버 요청을 하기 때문에 새로고침이 일어나 리액트에서는 올바르게 작동하지 않는다.
export default function Login() {
function handleSubmit() {
console.log("submitted!");
}
return (
<form>
<h2>Login</h2>
<div className="control-row">
<div className="control no-margin">
<label htmlFor="email">Email</label>
<input id="email" type="email" name="email" />
</div>
<div className="control no-margin">
<label htmlFor="password">Password</label>
<input id="password" type="password" name="password" />
</div>
</div>
<p className="form-actions">
<button className="button button-flat">Reset</button>
<button className="button" onClick={handleSubmit}>Login</button>
</p>
</form>
);
}
따라서 아래와 같이 코드를 짜서 새로고침을 막을 수 있다.
export default function Login() {
function handleSubmit(e) {
e.preventDefault();
console.log("submitted!");
}
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" />
</div>
<div className="control no-margin">
<label htmlFor="password">Password</label>
<input id="password" type="password" name="password" />
</div>
</div>
<p className="form-actions">
<button className="button button-flat">Reset</button>
<button className="button">
Login
</button>
</p>
</form>
);
}
상태를 이용한 사용자 값 변경
아래와 같이 객체로 묶어 만들어 줌으로써 관리해야 하는 상태와 함수를 줄일 수 있다.
식별자가 키포인트이다.
import { useState } from "react";
export default function StateLogin() {
const [enteredValues, setEnteredValues] = useState({
email: "",
password: "",
});
function handleInputChange(identifier, event) {
event.preventDefault();
setEnteredValues((prevValues) => ({
...prevValues,
[identifier]: event.target.value,
}));
}
function handleSubmit(event){
event.preventDefault();
console.log(enteredValues);
}
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)}
value={enteredValues.email}
/>
</div>
<div className="control no-margin">
<label htmlFor="password">Password</label>
<input
id="password"
type="password"
name="password"
onChange={(event) => handleInputChange("password", event)}
value={enteredValues.password}
/>
</div>
</div>
<p className="form-actions">
<button className="button button-flat">Reset</button>
<button className="button">Login</button>
</p>
</form>
);
}
Ref를 이용한 사용자 값 변경
ref를 이용하면 코드를 줄일 수 있다는 장점이 있지만 입력 갯수가 늘어나면 일일히 모두 연결해 주어야 하기 때문에
관리가 복잡해질 수 있다는 단점이 있다.
import { useState } from "react";
import { useRef } from "react";
export default function RefLogin() {
const password = useRef();
const email = useRef();
function handleSubmit(event) {
event.preventDefault();
console.log(password.current.value);
console.log(email.current.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" ref={email} />
</div>
<div className="control no-margin">
<label htmlFor="password">Password</label>
<input id="password" type="password" name="password" ref={password} />
</div>
</div>
<p className="form-actions">
<button className="button button-flat">Reset</button>
<button className="button">Login</button>
</p>
</form>
);
}
FormData를 통해 더 복잡한 양식의 데이터 가져오기
import { useState } from "react";
export default function Signup() {
const [passwordAreNotEqual, setPasswordAreNotEqual] = useState(false);
function handleSubmit(event) {
event.preventDefault();
const fd = new FormData(event.target);
//const enteredEmail = fd.get('email');
const acquisitionChannel = fd.getAll("acquisition"); //따로 가져오기
const data = Object.fromEntries(fd.entries()); //배열로 데이터 가져오기
data.acquisition = acquisitionChannel; //데이터 객체를 합치기
//제출했을 때 유효성 검사하기
if (data.password !== data["confirm-password"]) {
setPasswordAreNotEqual(true);
return;
}
setPasswordAreNotEqual(false);
event.target.reset();
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" 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
/>
<div className="control-error">
{passwordAreNotEqual && <p>Please must match.</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">
<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>
);
}
매 상태 유효성 검사하기
유효성 검사는 매 입력마다 확인해야 하기 떄문에 state를 활용한 코드에서 하고자 한다.
또한 onBlur을 통해 더 상세한 유효성 검사를 구현했다.
import { useState } from "react";
export default function StateLogin() {
const [enteredValues, setEnteredValues] = useState({
email: "",
password: "",
});
//포커싱을 잃으면 보이게끔 하기 위한 state
const [didEdit, setDidEdit] = useState({
email: false,
password: false,
});
const emailIsInvalid = didEdit.email && !enteredValues.email.includes("@");
function handleInputChange(identifier, event) {
event.preventDefault();
setEnteredValues((prevValues) => ({
...prevValues,
[identifier]: event.target.value,
}));
//다시 입력을 시작하면 false로 바꾸어 준다.
setDidEdit((prevEdit) => ({
...prevEdit,
[identifier]: false,
}));
}
function handleSubmit(event) {
event.preventDefault();
console.log(enteredValues);
}
function handleInputBlur(identifier) {
setDidEdit((prevEdit) => ({
...prevEdit,
[identifier]: 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")}
onChange={(event) => handleInputChange("email", event)}
value={enteredValues.email}
/>
<div className="control-error">
{emailIsInvalid && <p>Please enter a valid email address</p>}
</div>
</div>
<div className="control no-margin">
<label htmlFor="password">Password</label>
<input
id="password"
type="password"
name="password"
onBlur={() => handleInputBlur("password")}
onChange={(event) => handleInputChange("password", event)}
value={enteredValues.password}
/>
</div>
</div>
<p className="form-actions">
<button className="button button-flat">Reset</button>
<button className="button">Login</button>
</p>
</form>
);
}
제출 기반의 유효성 검사하기
제출 시에만 유효성을 검사하는 코드이다. 위 아래 모두 장단점이 있어 선택의 영역이지만
제출 기반의 유효성 검사는 항상 있는 편이 좋다.
import { useState } from "react";
import { useRef } from "react";
export default function RefLogin() {
const [emailInvalid, setEmailInvalid] = useState(false);
const password = useRef();
const email = useRef();
function handleSubmit(event) {
event.preventDefault();
const enteredEmail = email.current.value;
const emailIsInvalid = enteredEmail.includes('@');
if (!emailIsInvalid) {
setEmailInvalid(true);
return;
}
setEmailInvalid(false);
console.log("sending HTTP request...");
}
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={email} />
<div className="control-error">
{emailInvalid && <p>Please enter a valid email address</p>}
</div>
</div>
<div className="control no-margin">
<label htmlFor="password">Password</label>
<input id="password" type="password" name="password" ref={password} />
</div>
</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;
}
상태를 기반으로 한 파일의 input 컴포넌트화
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";
import { isEmail, isNotEmpty, hasMinLength } from "../util/validation";
export default function StateLogin() {
const [enteredValues, setEnteredValues] = useState({
email: "",
password: "",
});
const [didEdit, setDidEdit] = useState({
email: false,
password: false,
});
const emailIsInvalid =
didEdit.email &&
!isEmail(enteredValues.email) &&
!isNotEmpty(enteredValues.email);
const passwordIsInvalid =
didEdit.password && !hasMinLength(enteredValues.password, 6);
function handleInputChange(identifier, event) {
event.preventDefault();
setEnteredValues((prevValues) => ({
...prevValues,
[identifier]: event.target.value,
}));
setDidEdit((prevEdit) => ({
...prevEdit,
[identifier]: false,
}));
}
function handleSubmit(event) {
event.preventDefault();
console.log(enteredValues);
}
function handleInputBlur(identifier) {
setDidEdit((prevEdit) => ({
...prevEdit,
[identifier]: true,
}));
}
return (
<form onSubmit={handleSubmit}>
<h2>Login</h2>
<div className="control-row">
<Input
id="email"
type="email"
name="email"
//포커스를 잃을 때 보여줌
onBlur={() => handleInputBlur("email")}
onChange={(event) => handleInputChange("email", event)}
value={enteredValues.email}
error={emailIsInvalid && "Please enter a valid email!"}
></Input>
<Input
id="password"
type="password"
name="password"
onBlur={() => handleInputBlur("password")}
onChange={(event) => handleInputChange("password", event)}
value={enteredValues.password}
error={passwordIsInvalid && "Please enter a valid password!"}
></Input>
</div>
<p className="form-actions">
<button className="button button-flat">Reset</button>
<button className="button">Login</button>
</p>
</form>
);
}
Custom Hook을 활용하여 외부로 독립적인 상태 만들기
useInput.jsx
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) {
event.preventDefault();
setEnteredValue(event.target.value);
setDidEdit(false);
}
function handleInputBlur() {
setDidEdit(true);
}
return {
value: enteredValue,
handleInputBlur,
handleInputChange,
hasError: didEdit && !valueIsValid,
};
}
StateLogin.jsx
import { useState } from "react";
import Input from "./Input";
import { isEmail, isNotEmpty, hasMinLength } from "../util/validation";
import useInput from "../hooks/useInput";
export default function StateLogin() {
const {
value: emailValue,
handleInputChange: handleEmailChange,
handleInputBlur: handleEmailBlur,
hasError: emailIsInvalid,
} = useInput("", (value) => {
return isEmail(value) && isNotEmpty(value);
});
const {
value: passwordValue,
handleInputChange: handlePasswordChange,
handleInputBlur: handlePasswordBlur,
hasError: passwordIsInvalid,
} = useInput("", (value) => {
return hasMinLength(value, 6);
});
function handleSubmit(event) {
event.preventDefault();
if (emailIsInvalid || passwordIsInvalid) return;
console.log(emailValue, passwordValue);
}
return (
<form onSubmit={handleSubmit}>
<h2>Login</h2>
<div className="control-row">
<Input
id="email"
type="email"
name="email"
//포커스를 잃을 때 보여줌
onBlur={handleEmailBlur}
onChange={handleEmailChange}
value={emailValue}
error={emailIsInvalid && "Please enter a valid email!"}
></Input>
<Input
id="password"
type="password"
name="password"
onBlur={handlePasswordBlur}
onChange={handlePasswordChange}
value={passwordValue}
error={passwordIsInvalid && "Please enter a valid password!"}
></Input>
</div>
<p className="form-actions">
<button className="button button-flat">Reset</button>
<button className="button">Login</button>
</p>
</form>
);
}
반응형
'코딩 정보 > React' 카테고리의 다른 글
[React] 재활용성이 좋은 modal 만들기 (0) | 2024.08.10 |
---|---|
[React] useEffect의 실행 시점에 대해 알아보자 (0) | 2024.08.09 |
[React] Custom Hook에 대해 알아보자 (0) | 2024.07.31 |
[React] Http 통신을 해보자 (0) | 2024.07.30 |
[React] 랜더링 최적화 방법을 자세히 알아보자 (0) | 2024.07.25 |