보통 검증할 때 material + formik 조합으로 많이 사용하는데, 기존 formik은 무언가 버튼을 눌러서 submit 요청을 보내야 그 밑에 helperText가 뜨는 구조였다.
하지만, 내가 원하는 건 onChange 시에 바로바로 검증을 하여 helperText가 뜨는 구조를 원했다.
그래서 아래처럼 노가다를 진행했다.💤
Input Component
props
- type : input의 type
- label : input의 제목
- value : input의 value
- maxValue : input이 가질 수 있는 최대 글자수
- setValue : value가 바뀌면 부모컴포넌트에있는 state에 저장시키기 위한 함수
- regexCheck 또는 check함수 둘 중에 하나만 있으면 됩니다.
- regexCheck : validation 할 정규표현식
- successText : test 통과했을 때 나타나는 문구
- errorText : test 실패했을 때 나타나는 문구
- defaultText : 기본값 또는 빈값일때 나타나는 문구
state
- isError : true면 material의 TextField의 error 기능을 작동시키는 역할. (이를 통해, 인풋의 색깔이 빨갛게 바뀐다.)
- helperText : helperText를 저장하고, 변경시키는 역할
import React, { useState } from "react";
import styled from "@emotion/styled";
import { TextField } from "@mui/material";
export default function ValidationInput({
label,
type,
value,
maxValue,
setValue,
regexCheck,
successText,
errorText,
defaultText
}) {
const [isError, setIsError] = useState(true);
const [helperText, setHelperText] = useState(defaultText);
const HandleOnChange = (e) => {
//최대값이 지정되어있으면 value를 저장하지 않는다.
if (maxValue && maxValue < e.target.value.length) return;
setValue(e.target.value);
//공백인 경우 defaultText로 바꾼다.
if (e.target.value === "") {
setIsError(true);
return setHelperText(defaultText);
}
if (regexCheck) {
// 정규표현식체크가 통과되면 successText를 송출하고 아니면 errorText를 송출한다
if (regexCheck.test(e.target.value)) {
setIsError(false);
return setHelperText(successText);
}
if (!regexCheck.test(e.target.value)) {
setIsError(true);
setHelperText(errorText);
}
}
};
return (
<div>
<Label>{label}</Label>
<TextField
error={isError}
id="standard-error-helper-text"
helperText={helperText}
variant="standard"
type={type}
onChange={HandleOnChange}
value={value}
/>
</div>
);
}
ValidationInput.defaultProps = {
type: "text",
label: "",
value: ""
};
const Label = styled.span`
color: #878787;
font-size: 18px;
`;
Regex
정규식을 통하여 검사를 하게끔 하였다.
//한국어+글자수(3글자 이상,10글자 이하)
const nickname = /^[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]{3,10}$/;
//email형식
const email = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
//영어소문자+숫자+특수문자(_,-)+글자수(6글자 이상, 10글자 이하)
const password = /^[a-z0-9_-]{6,10}$/;
const regex = { nickname, email, password };
export default regex;
Parent Component
Input Components에 필요한 정보를 하나하나씩 보내주었다.
import React, { useState } from "react";
import ValidateInput from "../src/Components/Shared/ValidateInput";
import regex from "../src/Shared/regex";
const App = () => {
const [nickInput, setNickInput] = useState("");
const [emailInput, setEmailInput] = useState("");
const [passwordInput, setPasswordInput] = useState("");
return (
<>
<ValidationInput
label="닉네임"
value={nickInput}
setValue={setNickInput}
maxValue={10}
regexCheck={regex.nickname}
defaultText="닉네임을 입력해주세요!"
successText="통과"
errorText="한글사랑 나라사랑, 3글자 이상!"
/>
<ValidationInput
label="이메일"
value={emailInput}
setValue={setEmailInput}
regexCheck={regex.email}
defaultText="이메일을 입력해주세요!"
successText="통과"
errorText="이메일 양식!"
/>
<ValidationInput
label="패스워드"
type="password"
value={passwordInput}
setValue={setPasswordInput}
regexCheck={regex.password}
maxValue={10}
defaultText="이메일을 입력해주세요!"
successText="통과"
errorText="소문자, 6글자 이상!"
/>
</>
);
};
export default App;
완성
한 번 더 Develop! 🙄
중복확인 버튼을 만들어서 서버랑 통신하는 api를 연결할 수 있게 더 develop을 했다. disabled 속성을 통하여, 중복체크에 통과되지 않으면 버튼이 눌려지지 않도록 했다.
InputComponent
import React, { useState } from "react";
import styled from "@emotion/styled";
import { Button, TextField } from "@mui/material";
//handleValueCheck는 중복확인을 할 수 있는 api함수를 담아주면 됩니다.
//isCheck는 부모로부터 중복확인 여부 state 값을 받아온다.
//setIsCheck는 부모로부터 중복확인 여부 state를 변경시킬 수 있는 함수를 받아온다.
export default function ConfirmValidationInput({
label,
type,
value,
maxValue,
setValue,
regexCheck,
successText,
errorText,
defaultText,
handleValueCheck,
isCheck,
setIsCheck
}) {
const [isError, setIsError] = useState(true);
const [isOnCheck, setIsOnCheck] = useState(false); //중복체크를 on 할 것인지 안할것인지 판별 여부
const [helperText, setHelperText] = useState(defaultText);
const HandleOnChange = (e) => {
//한번이라도 수정한 적이 있으면 isWrite를 true로, isCheck를 false 변경시킨다.
if (isCheck) setIsCheck(false);
//최대값이 지정되어있으면 value를 저장하지 않는다.
if (maxValue && maxValue < e.target.value.length) return;
setValue(e.target.value);
//공백인 경우 defaultText로 바꾼다.
if (e.target.value === "") {
setIsError(true);
return setHelperText(defaultText);
}
if (regexCheck) {
// 정규표현식체크가 통과되면 successText를 송출하고 아니면 errorText를 송출한다
if (regexCheck.test(e.target.value)) {
setIsError(false);
setIsOnCheck(true);
return setHelperText(successText);
}
if (!regexCheck.test(e.target.value)) {
setIsError(true);
setHelperText(errorText);
setIsOnCheck(false);
}
}
};
const handleCheck = () => {
handleValueCheck();
};
return (
<Container>
<Label>{label}</Label>
<Input
error={isError}
id="standard-error-helper-text"
helperText={helperText}
variant="standard"
type={type}
onChange={HandleOnChange}
value={value}
/>
{isCheck ? (
<CheckSuccessBnt>확인</CheckSuccessBnt>
) : (
<CheckBnt isOnCheck={isOnCheck} disabled={!isOnCheck ? true : false} onClick={handleCheck}>
중복확인
</CheckBnt>
)}
</Container>
);
}
ConfirmValidationInput.defaultProps = {
type: "text",
label: "",
value: "",
setValue: () => {},
isCheck: false,
setIsCheck: () => {},
handleValueCheck: () => {}
};
const Container = styled.div`
position: relative;
`;
const Label = styled.span`
color: #878787;
font-size: 18px;
`;
const Input = styled(TextField)`
width: 100%;
input {
width: calc(100% - 110px);
}
`;
const CheckBnt = styled(Button)`
position: absolute;
right: 0;
top: 10px;
width: 100px;
height: 40px;
border: ${({ isOnCheck }) => (isOnCheck ? "1px solid #ff7775;" : "1px solid #d9d9d9")};
color: ${({ isOnCheck }) => (isOnCheck ? "#FF7775" : "#3C3C3C")};
font-size: 16px;
border-radius: 100px;
`;
const CheckSuccessBnt = styled(Button)`
display: flex;
align-items: center;
width: 100px;
height: 40px;
position: absolute;
right: 0;
top: 10px;
border: 1px solid #6b95ff;
font-size: 16px;
border-radius: 100px;
`;
Parent Component
import React, { useState } from "react";
import styled from "@emotion/styled";
import regex from "../src/Shared/regex";
import ConfirmValidationInput from "../src/Components/Shared/ConfirmValidationInput";
const mypage = () => {
const [value, setValue] = useState("");
const [isCheck, setIsCheck] = useState(false);
return (
<Container>
<ConfirmValidationInput
label="이메일"
value={value}
setValue={setValue}
isCheck={isCheck}
setIsCheck={setIsCheck}
handleValueCheck={() => {
alert("인증완료!");
setIsCheck(true);
}}
regexCheck={regex.email}
defaultText="이메일을 입력해주세요!"
successText="통과"
errorText="이메일 양식!"
/>
</Container>
);
};
const Container = styled.div``;
export default mypage;
'💻 코드공방' 카테고리의 다른 글
재사용 가능한 react-hook-form 컴포넌트 만들기 (+ typescript, tailwind) (1) | 2022.12.01 |
---|---|
url이 바뀌면 메뉴 버튼 닫기 (0) | 2021.08.09 |
인풋체크박스를 활용하여 필터링검색 구현하기 (0) | 2021.07.21 |