๋ฆฌํฉํ ๋ง ์ดํ ๋ฐ๋ชจ
input์ด ๋น์ด์์๋ + ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ๋ชจ๋ ํต๊ณผํ๊ธฐ ์ ์๋ ๋ก๊ทธ์ธ ๋ฒํผ์ด disabled ๋์ด ๋ฒํผ์ ๋๋ฅผ ์ ์๋ค.
๋ชจ๋ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํต๊ณผํด์ผ์ง ๋ก๊ทธ์ธ ๋ฒํผ์ ๋ถ์ด ๋ค์ด์ค๊ณ ๋ก๊ทธ์ธ ๋ฒํผ์ ํด๋ฆญํด์ ์๋ฒ์ ๋ก๊ทธ์ธ ์์ฒญ์ ๋ณด๋ธ๋ค.
์ด๋ฒ ํ๋ก์ ํธ์์๋ ๋ก๊ทธ์ธ, ํ์๊ฐ์ ๊ธฐ๋ฅ ๊ตฌํ์ ์ ํจ์ฑ ๊ฒ์ฌ๊ฐ ํ์ํ ๋ชจ๋ input์ฐฝ์ useState hook ์ํ๋ก ๊ด๋ฆฌํ ํ์์์ด, react-hook-form ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋์ ํด์ form์ ์์ฑํ๋ค. react-hook-form์ ์ฌ์ฉํ๋ฉด ์๋ฒ๋ก ํต์ ์ด ๊ฐ๊ธฐ ์ด์ ์ ํด๋ผ์ด์ธํธ์์ ํด์ผ๋๋ ์ ํจ์ฑ๊ฒ์ฌ๋ฅผ ์ฝ๊ณ ๊ฐ๋จํ ์ฝ๋๋ก ์งค ์ ์๋ค๋ ์ฅ์ ์ด ์๋ค.
react-hook-form ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ณต์ ๋ฌธ์์ ํ์ดํ๋ Simple React forms validation ์ด๋ค. ๊ทธ๋งํผ form์ ๊ฐ๋ฐํ ๋ validation์ ์ต์ ํํ ์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ผ๋ ๋ป
๋ฆฌํฉํ ๋ง ์ด์ ์ด์
ํ์ง๋ง hook form์ ์ฒ์ ๊ณต๋ถํ๋ ์ ๋ ์ฝ์ง๋ง์ ์์๋๋ฐ..
๋ด๊ฐ ์ํ๋๋๋ก ์๋์ด ์๋๋ ๊ฒฝ์ฐ๊ฐ ๊ฝค ์์๋๋ฐ ๋ํ์ ์ธ ์๊ฐ input์ด ์์ ๋น์ด์๋ ๊ฒฝ์ฐ์ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ด๋ค. ์๋จ์ ๋ฆฌํฉํ ๋ง ์ดํ ๋ฐ๋ชจ์ฒ๋ผ ์๋๋ input์ด ๋น์ด์์ ๋๋ ์ ํจ์ฑ ๊ฒ์ฌ๊ฐ ๋ชจ๋ ํต๊ณผํด์ผ ๋ก๊ทธ์ธ ๋ฒํผ์ ๋ถ์ด ๋ค์ด์ค๊ฒ ํ๊ณ ์ถ์๋๋ฐ, ํ๋ก์ ํธ๊ฐ ๋๋ ๋๊น์ง ์ด๋ถ๋ถ์ ์ ๊ฒฝ์ ๋ชป์จ์ ์ฐ์ ์ ์ด ์ํ๋ก ๋ฐฐํฌ๋ฅผ ํ์๋ค.
์ฆ, ์ด์ ์ฝ๋๋ input๊ฐ์ด empty์ฌ๋ ๋ฒํผ์ด ๋๋ฌ์ง๊ณ , ๋น์ด์๋ data ๊ฐ์ฒด {email: '', password: ''} ์ ๋ณด๋ก ์๋ฒ์ post ์์ฒญ๊น์ง ๋ณด๋ด๋ ๋นํจ์จ์ ์ธ ๋ฌธ์ ๊ฐ ์์๋ค. ๊ทธ๋ฌ๋ ์ด ์ํ์์๋ ์๋ฒ db์๋ ๋น์ฐํ ๋น์ด์๋ email๊ณผ ํจ์ค์๋ ์ ๋ณด๊ฐ ์๊ณ ! ์๋ฒ๋ ์ ๋ณด๊ฐ ์ผ์นํ์ง์์ผ๋ ์๋ฌ๋ฉ์์ง๋ฅผ ์ฃผ๊ณ , ํด๋ผ์ด์ธํธ์์๋ ๊ทธ ์๋ฌ๋ก ์ธ์ํ์ฌ ์๋ฌ๋ฉ์์ง "์์ด๋ ํน์ ๋น๋ฒ์ด ์ผ์นํ์ง ์๋๋ค" ๋ฅผ ๋ณด์ฌ์ฃผ๋ ์์ ์ด์๊ฐ ์์๋ค.
๋ฆฌํฉํ ๋ง ์ดํ ์ฝ๋
const {
register,
handleSubmit,
formState: { isSubmitting, errors, isValid },
} = useForm({ mode: "onChange" });
useForm ์ onChange ๋ชจ๋๋ฅผ ์ฃผ๋ฉด ์ค์๊ฐ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํ๊ฒ๋๊ณ errors๊ฐ์ฒด ๋ํ ์ค์๊ฐ์ผ๋ก ์ ๋ฐ์ดํธ ๋๋ค.
<input
autoFocus
id="email"
type="email"
name="email"
placeholder="์ด๋ฉ์ผ์ ์ ๋ ฅํด์ฃผ์ธ์"
{...register("email", {
required: true,
pattern: {
value: /\S+@\S+\.\S+/,
message: "์ฌ๋ฐ๋ฅธ ์ด๋ฉ์ผ ํ์์ด ์๋์์",
},
})}
></input>
์ด๋ฉ์ผ๊ณผ ๋น๋ฐ๋ฒํธ input register์ ๋ชจ๋ required: true ๋ฅผ ์ถ๊ฐํด์ ํ์ ์กฐ๊ฑด์ผ๋ก ๋ง๋ ๋ค.
<Button disabled={!isValid || isSubmitting}>
๋ฒํผ์ disabled ์์ฑ์ ์ถ๊ฐํด์ฃผ๊ณ ,
!isValid - ์๋ฌ๊ฐ ์๋ ๊ฒฝ์ฐ์ (์ ํจ์ฑ ๊ฒ์ฌ์ ํ๋๋ผ๋ ๊ฑธ๋ฆฌ๋ฉด errors ๊ฐ์ฒด์ ๊ฐ์ด ์๊ฒจ์ isValid ์ํ๋ false๊ฐ ๋๋ค)
isSubmitting - ํผ์ ์ ์ถ ์ค์ผ๋
๋นํ์ฑํ ์ํจ๋ค.
&:disabled {
background-color: var(--gray-300);
cursor: none;
}
๋ฌผ๋ก Button CSS์๋ disabled ์์ฑ์ ์ค๋ค.
์ปค์๊ฐ ์์ ์์๊ธฐ๊ฒ ํด์ฃผ๊ณ ๋ฒํผ ์ปฌ๋ฌ๋ ํ์์ผ๋ก ๋ฐ๊ฟ์ค๋ค.
์ ์ฒด ์ฝ๋
//LoginForm.js
import axios from "axios";
import { useForm } from "react-hook-form";
import Button from "../../common/Button";
import { LogInFormContainer } from "./LogInFormStyle";
import { Link, useNavigate } from "react-router-dom";
import { useSelector, useDispatch } from "react-redux";
import { setWarningToast, setNoticeToast } from "../../../features/toastSlice";
const LogInForm = () => {
const navigate = useNavigate();
const dispatch = useDispatch(); //for redux dispatch
const {
register,
handleSubmit,
formState: { isSubmitting, errors, isValid },
} = useForm({ mode: "onChange" });
//isValid: errors ๊ฐ์ฒด ๋น์ด์์ผ๋ฉด true
const onSubmit = (data) => {
// console.log(data); //{email: 'test@email.com', password: '๋น๋ฒ์ ๋ณด'}
// ๋ก๊ทธ์ธ ์์ฒญ
axios
.post("/api/login", data)
.then((response) => {// console.log(response); //{data: {…}, status: 200, statusText: 'OK', headers: {…}, config: {…}, …}
...๋ก๊ทธ์ธ ๋ก์ง ์๋ต...
})
.catch((error) => {
console.log(error);
if (error.response.status === 401) {
// alert์ฐฝ ๋์ฒด
dispatch(
setWarningToast({
message: `์์ด๋ ํน์ ๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ์ง ์์์\n`,
})
);
}
});
}
return (
<LogInFormContainer>
<form onSubmit={handleSubmit(onSubmit)}>
<label>์ด๋ฉ์ผ</label>
<input
autoFocus
id="email"
type="email"
name="email"
placeholder="์ด๋ฉ์ผ์ ์
๋ ฅํด์ฃผ์ธ์"
{...register("email", {
required: true,
pattern: {
value: /\S+@\S+\.\S+/,
message: "์ฌ๋ฐ๋ฅธ ์ด๋ฉ์ผ ํ์์ด ์๋์์",
},
})}
></input>
{errors.email && <span>{errors.email.message}</span>}
<label>๋น๋ฐ๋ฒํธ</label>
<input
id="password"
type="password"
name="password"
placeholder="๋น๋ฐ๋ฒํธ๋ฅผ ์
๋ ฅํด์ฃผ์ธ์"
{...register("password", {
required: true,
minLength: {
value: 8,
message: "๋น๋ฐ๋ฒํธ๋ 8์ ์ด์์ด์์",
},
})}
></input>
{errors.password && <span>{errors.password.message}</span>}
<Button disabled={!isValid || isSubmitting}>
๋ก๊ทธ์ธ
</Button>
</form>
</LogInFormContainer>
);
};
export default LogInForm;
//Button.js
import styled from "styled-components";
export const Button = styled.button`
display: flex;
justify-content: center;
align-items: center;
border-radius: 10px;
border: none;
width: 300px;
height: 35px;
background-color: var(--fridge-500};
color: var(--white);
font-size: 16px;
margin: 20px 0 0 0;
&:disabled {
background-color: var(--gray-300);
cursor: none;
}
`;
๋์ ๊ฒฝ์ฐ ์ด๋ ๊ฒ ๋ฐ๊ฟ์ฃผ๋ ์ํ๋๋๋ก ์ ์๋ํ๊ฒ ๋์๋ค.
์ฝ๊ณ ๊ฐ๋จํ๊ฒ ๋ฆฌ์กํธ form์ ๊ตฌํํ ์ ์๋ค๋๋ฐ ์ฒ์์ ์์ํ๊ธฐ๋ํด์ ์ด๊ฒ์ ๊ฒ ๊ฝค๋ ์ ๋ฅผ ๋จน์๋๋ฐ, ๋๊ตฐ๊ฐ์๊ฒ๋ ๋์์ด ๋์์ผ๋ฉด ์ข๊ฒ ๋ค.
'React' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
npm install -g ์๋ฌ EACCES (1) | 2023.01.05 |
---|---|
๋ฆฌ์กํธ App.js useEffect ๋น๋๊ธฐ ํธ์ถ์์ ์ด์ ํด๊ฒฐ Suspense lazy (0) | 2022.11.30 |
ํ๋ก ํธ์๋ ๊ธฐ์ ๋ฉด์ ๊ธฐ์ด ์์์ง๋ฌธ ๋ต๋ณ ์ ๋ฆฌ (0) | 2022.11.27 |
VSCode ํน์ ๊ธ์ ์ ๋ณ๊ฒฝ (0) | 2022.10.12 |
๋ฆฌ์กํธ | ๋ฉ๋ด ํญ์ useParams ๋ผ์ฐํ ์ผ๋ก ๊ด๋ฆฌ (0) | 2022.10.11 |