이제 클래스형 말고 함수형 컴포넌트를 hook과 함께 사용해 비슷한 일을 해보자.
회원가입 기능을 함수형 컴포넌트를 이용해 구현해볼 것이다.
새 react 폴더를 만든 후, src 폴더 안에 Form.jsx를 만들고, App에서 이를 import하자.
생성될 폴더명은 여기서는 practice로 정했다.
npm init -y
npx create-react-app practice
/* practice/src/Form/From.jsx */
import React from 'react'
const Form = () => {
return(
<form>
<ul>
<li>
<label htmlFor='userid'>userid</label>
<lnput type='text' name='userid'/>
</li>
<li>
<label htmlFor='userpw'>password</label>
<input type='text' name='userpw'/>
</li>
<li><input type='submit' value='register'></li>
</ul>
</form>
)
}
export default Form
보는것처럼 class가 아닌 화살표 함수로 컴포넌트를 만든 후, 이를 export 했다.
form element에 다음과 같이 submitHandler를 추가한다. (여기까진 클래스 컴포넌트와 동일)
/* practice/src/Form/Form.jsx */
const Form = () => {
const submitHandler= (e) => {
e.preventDefault()
}
return(
<form onSubmit={submitHandler}>
// ... 중략
</form>
)
}
이제 id, pw를 바꿔줘야 하는데, 클래스형 컴포넌트에서는 state를 선언하고,
그 안에 변할 수 있는 값들을 전부 넣은 후, state를 바꿔주는 식으로 진행이 되었다.
함수형 컴포넌트는 useState라는 함수라는 hook을 사용해 동일한 기능을 수행한다.
hook은 react의 내장 hook과 우리가 직접 만들어 사용할 수 있는 custom hook이 있는데,
우선 여기서는 내장 hook을 먼저 배워보도록 하자.
react를 import하는 첫 줄에서 다음과 같이 useState 함수를 가져온다.
/* practice/src/Form/Form.jsx */
import React, {useState} from 'react'
이름을 보면 알 수 있듯 useState는 state를 바꿀 수 있도록 해주는 가장 기본적인 훅이다.
다음과 같이 Form 함수에 이를 활용해보자.
일반적으로 useState 함수를 사용할 땐
const [바뀔변수, 변수를 바꿔줄 함수] = useState([바뀔 변수의 초기값])
과 같은 형식으로 선언된다.
예를 들어 userid, userpw 각각에 대해 useState를 이용, setUserid, setUserpw를 선언했다.
/* practice/src/Form/Form.jsx */
const Form = () => {
const [ userid, setUserid ] = useState('')
const [ userpw, setUserpw ] = useState('')
}
이러면 초기값 ''을 갖는 userid를 바꿔줄 함수 setUserid를 선언한 것이다.
이제 각각의 input에 다음과 같이 변화에 대응하는 함수를 넣는다.
/* practice/src/Form/Form.jsx */
<input type='text' name='userid' onChange={changeUserid}/>
//...중략
<input type='password' name='userpw' onChange={changeUserpw}/>
ChangeUSerid, ChangeUserpw는 다음과 같이 useState를 이용해 선언해준 setUserid, setUserpw를 포함한다.
/* practice/src/Form/Form.jsx */
const changeUserid = (e) => {
const value = e.target.value
setUserid(value)
}
const changeUserpw = (e) => {
const value = e.target.value
setUserpw(value)
}
여기까지하면 우리가 id, pw를 입력하면 그걸 인식해서 userid, userpw의 값을 바꿔주는 것을 확인할 수 있다.
잘 되는지 확인하려면 submitHandler에 다음과 같이 변수를 console.log로 불러와보면 된다.
/* practice/src/Form/Form.jsx */
const submitHandler = (e) => {
e.preventDefault()
console.log(userid, userpw)
}
그런데 잘 보면 userid를 인식해 바꾸는 함수나 userpw를 바꾸는 함수나 변수명만 같을 뿐
완전히 동일한 기능을 가지고 있다.
이 두 함수를 하나로 통합하는 작업을 해보자.
우선 필요한 두 변수 userid, userpw를 하나의 객체로 묶고 각 input의 name을 통해
우리가 input에 정보를 타이핑할때 어떤 변수의 값이 변할지를 name값을 통해 구분해준다.
/* practice/src/Form/Form.jsx */
import React, {useState} from 'react'
const Form = () => {
const [userInfo, setUserInfo] = useState({userid:'', userpw;''})
// SetUserid, setUserpw를 하나로 통합
const submitHandler = (e) =>{
e.preventDefault()
console.log(userid, userpw)
}
const changeInfo = (e) => {
const value = e.target.value
const name = e.target.name
setUserInfo({...userInfo, [name]:value})
}
// changeUserid, changeUserpw를 하나로 통합
return(
<form onSubmit={submitHandler}>
<ul>
<li>
<label htmlFor='userid'>userid</label>
<input type='text' name='userid' onChange={changeUserid}/>
</li>
<li>
<label htmlFor='userpw'>userpw</label>
<input type='text' name='password' onChange={changeUserpw}/>
</li>
<li><input type='submit' value='submit'/></li>
</ul>
</form>
)
}
export default Form
changeInfo 함수를 보면 함수 안에서 value와 name을 target의 value, name으로 지정해 값을 준다.
name을 통해 우리는 id, pw중 뭐가 바뀌는지를 특정해 줄 수 있다.
id를 바꾼다면 해당 input의 name인 userid가 e.target.name의 값이 될 것이다.
이제 setUserInfo를 보면 인자값에 원래의 값인 userInfo를 깊은 복사해온 것 다음에
[name]: value 라는 것이 추가되었는데,
이는 name의 값을 지정해 그 name의 value만을 바꿔주겠다는 것을 의미한다.
userid 입력에 뭔가 입력하면 changeInfo가 발동해 value, name을 지정하고,
초기값인 객체 {userid:'', userpw:''} 에서 [name]과 동일한 값을 가진 userid의 value만이 변한다.
여기서 한 단계 더 나가서 입력값에 대한 함수를 아예 Form 컴포넌트 밖으로 분리해보자.
/* practice/src/Form/Form.jsx */
import React, {useState} from 'react'
const useInput = (defaultValue) => {
const [ userInfo, setUserInfo ] = useState(defaultValue)
const onChange = (e) => {
setUserInfo(e.target.value)
}
return {
userInfo,
onChange
}
}
useInput 함수를 실행하면 return 값으로 userInfo값과 onChange 함수를 돌려준다.
이를 각각의 input에 attribute로 전달해줄 것이다.
이 함수를 바로 줄 수는 없고, 변수에 넣는 단계를 거쳐야 한다.
/* practice/src/Form/Form.jsx */
const Form = () => {
const id = useInput('')
const pw = useInput('')
const SubmitHandler (e) => {
//...중략
}
return(
//...중략
<input type='text' name='userid' {...id}/>
//... 중략
<input type='password' name='userpw' {...pw}/>
)
}
id, pw에 각각 useInput을 넣어주면 변수 안에는 useInput의 return값인 userInfo와 onChange 함수가
객체 형태로 담긴다.
이 객체를 깊은 복사해서 input element에 넣어주면 결과적으로 element에 함수와 값이 통째로 전달된다.
지금까지 한 작업물을 정리하면 다음과 같이 코드가 작성되었을 것이다.
/* practice/src/Form/Form.jsx */
import React, {useState} from 'react'
const useInput = (defaultValue) => {
const [userInfo, setUserInfo] = useState(defaultValue)
const onChange = (e) => {
setUserInfo(e.target.value)
}
return {
userInfo,
onChange
}
}
const Form = () => {
const id = useInput('')
const pw = useInput('')
// 동일한 함수를 각각에 대해 실행하면 다른 메모리를 차지하므로 각각이 다른 결과를 줌
const submitHandler = (e) =>{
e.preventDefault()
console.log(id.userInfo, pw.userInfo)
// 결과물을 확인하고 싶다면 다음과 같이 console.log로 불러온다.
}
return(
<form onSubmit={submitHandler}>
<ul>
<li>
<label htmlFor='userid'>userid</label>
<input type='text' name='userid' {...id} />
{/* input의 children들을 객체로 한 번에 묶어온다. */}
</li>
<li>
<label htmlFor='userpw'>userpw</label>
<input type='text' name='userpw' {...pw}/>
</li>
<li><input type='submit' value='submit'/></li>
</ul>
</form>
)
}
export default Form
마지막으로 입력한 id, pw가 조건에 맞는지를 검증하는 validate 함수를 추가해보자.
회원가입을 위해 id, pw를 입력후 submit 할 때,
id는 email 형식이고, pw는 8글자 이상을 포함하는 조건을 만족하면 가입이 잘 되었다는 알림 창을,
그렇지 않으면 실패 이유를 화면에 렌더링해주도록 하고싶다.
우선 검증을 해주는 함수를 선언한다.
/* practice/src/Form/Form.jsx */
const validate = ({userid, userpw}) => {
const errors = {}
if(!userid) {
errors.userid='email을 입력해주세요'
} else if (!/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/.test(userid)) {
errors.userid = 'invalid email address'
}
if (!userpw) {
errors.userpw = 'password를 입력해주세요'
} else if (userpw.length < 8) {
errors.userpw = 'password는 8글자 이상이어야 합니다.'
}
return errors
}
userid, userpw 각각에 대해 검증을 거친 후, error가 있다면 errors 객체에
에러 내용을 담아 return해주는 방식으로 작동한다.
이제 errors의 값을 바꿔줄 훅(useState)을 Form 함수 안에 추가한다.
/* practice/src/Form/Form.jsx */
const [errors, setErrors] = useState({})
// 초기값은 빈 객체, 이후 id/pw에 문제가 있다면 각각의 key, value가 추가된다.
이 setErrors 함수를 Form 함수의 submitHandler 함수안에서 실행한다.
/* practice/src/Form/Form.jsx */
const submitHandler = (e) => {
e.preventDefault()
const input = {
userid:id.userInfo,
userpw:pw.userInfo
}
setErrors(validate(input))
}
submit을 클릭하면 submitHandler가 발동된다.
여기서 입력한 id, pw값을 모아서 검증 함수 validate안에 매개변수로 넣어준다.
전달된 매개변수 id, pw에 대해 각각 검증 과정을 거치면 errors 객체가 완성되어 return 된다.
이 return된 값을 setErrors에 넣어 실행해 (Form안에서 선언한) errors의 값을 바꿔준다.
이제 이 errors의 내용을 사용자에게 보여줄 수 있다.
조건문을 이용해 에러가 있다면 그때만 그 내용을 보여주도록 코드를 추가한다.
/* practice/src/Form/Form.jsx */
//...중략
return(
<form onSubmit={submitHandler}>
<ul>
<li>
<label htmlFor='userid'>userid</label>
<input type='text' name='userid' {...id}/>
{errors.userid && <span>{errors.userid}</span>
</li>
<li>
<label htmlFor='userpw'>password</label>
<input type='password' name='userpw' {...pw}/>
{errors.userpw && <span>{errors.userpw}</span>}
</li>
<li><input type='submit' value='submit'></li>
</ul>
</form>
)
'React' 카테고리의 다른 글
#12 useContext (0) | 2022.04.29 |
---|---|
#11 함수형 컴포넌트 2. useEffect (0) | 2022.04.29 |
#9 댓글 기능 만들기 part2 (0) | 2022.04.24 |
#8 댓글 기능 만들기 part1 (0) | 2022.04.24 |
#7 곱셈 프로그램 만들기 (0) | 2022.04.24 |