본문 바로가기
React/React Basic

[React] Changing Complex State | 풀네임 받기 | form input prevValue

by CodeMia 2022. 3. 28.

 

Last Name, First Name 입력칸 만들고 풀 네임이

자동으로 <h1>에 들어가도록 해보자.

 

 

앞에서 배운 부분을 활용하면 다음과 같은 코드를 쓸 수 있다.

import React, { useState } from "react";

function App() {
const [fName, setFName] = useState("");
const [lName, setLName] = useState("");

function updateFName(event) {
const firstName = event.target.value;
setFName(firstName);
}

function updateLName(event) {
const lastName = event.target.value;
setLName(lastName);
}

return (
<div>
<form className="container">
<h1>
Hello {fName} {lName}
</h1>
<input onChange={updateFName} placeholder="First Name"></input>
<input onChange={updateLName} placeholder="Last Name"></input>
<button type="submit">Submit</button>
</form>
</div>
);
}

export default App;

 

 

 


 

하지만 위의 방법은 중복이 너무 많다. 

좀 더 간편하게 만들 수 있는 방법이 없을까? 

 

 

useState는 다음과 같이 바꿔 좀 더 간단하게 한 줄로 만들고 

 

 

아래 내용도 바꿔준다. 

 

 

 

함수도 하나로 합치기 

하지만 여기서 value가 fName 인지 lName 구분이 되지 않는다. 

 

first Name 칸에 입력한 후 

다시 last Name 칸에 이름 입력하면 앞 입력한 것이 삭제된다. 

둘을 구분하는 방법이 없을까? 

 

 

input에 name을 입력하고, 무엇이 있나 콘솔로 보면 

 

타이핑한 내용과 설정된 name이 나온다.

 

 

 

하나의 문제점은 이름을 입력했지만 이름 전체가 나오지 않고

글자가 하나씩 쪼개서 나온다는 것이다. 

 

 

이 것은 controlled components의 개념 때문이다. 

value를 fullName.fName으로 했는데 useState에서 setFullName을 함수가 아직 실행이 되지 않아 최초 이니셜 벨류만 보여진다. ??

we haven't actually got a way of setting that full name yet. So it's always showing the initial value which is just the empty string. so it's always showing the initial value which is just the empty string.

그리서 value를 커멘트 아웃 시키면 uncontrolled componets가 되어 이제 잘 입력이된다. 

 

 

 

 

 

 

if문으로 fName/ lName을 구분한 후 

입력을 하면 full Name이 나오지 않고 

또 앞 내용이 지워지는 것을 볼 수 있다. 

 

 

 

React Dev tools 을 확인해 보면 

inspect -> components 

hooks 에 State: {fName: "", lName: ""} 두 개 있는 것을 볼 수 있다. 

 

 

 

 

하지만 여기서 fName을 입력하고 나면 

 

fName만 남는 것을 볼 수 있다. 

 

 

 

이 전에 입력한 내용도 기억을 하고 있어야 한다. 

 

useState안 setFullName을 호출했을 때 리액트는 앱 컴포넌트를 리렌더를 할 것이다. 

앱은 state를 가지고 있는데 자바스크립트 오브젝트 안의 form안에 fullName에 있다. 

리액트는 이 오브젝트의 value를 기억한다. 이 뜻은 prevValue를 기억해서 fName 또는 lName으로 업데이트 되어야한다. 

 

 

 

setFullName을 부를 때 prevValue를 이용해서 이전 값을 기억하게 한다.

 

 

 

 

이제 풀네임이 입력이 된다. 

 

 

 

이제는 커멘트 아웃한 value를 다시 활성화 시킨다. 

이유: so that the values of our inputs can correspond to the lastest state, so that we control this input using our state. (??)

 

 

 

지금까지의 코드 

import React, { useState } from "react";

function App() {
const [fullName, setFullName] = useState({
fName: "",
lName: ""
});

function updateFullName(event) {
const newValue = event.target.value;
const inputName = event.target.name;

setFullName((prevValue) => {
if (inputName === "fName") {
return {
fName: newValue,
lName: prevValue.lName
};
} else if (inputName === "lName") {
return {
fName: prevValue.fName,
lName: newValue
};
}
});
}

return (
<div>
<form className="container">
<h1>
Hello {fullName.fName} {fullName.lName}
</h1>
<input
name="fName"
onChange={updateFullName}
placeholder="First Name"
value={fullName.fName}
></input>
<input
name="lName"
onChange={updateFullName}
placeholder="Last Name"
value={fullName.lName}
></input>
<button type="submit">Submit</button>
</form>
</div>
);
}

export default App;

 

 

 

하지만 위 코드를 더 줄일 수 있다. 

 

안의 이름도 맞춰서 바꾸준다.

 

setFullName 함수 안에서 event.target을 사용할 수는 없다. 

문법 에러가 난다. 

 

- synthetic event 참고 -

https://reactjs.org/docs/events.html

 

 

 

최종 코드

import React, { useState } from "react";

function App() {
const [fullName, setFullName] = useState({
fName: "",
lName: ""
});

function updateFullName(event) {
const {value, name} = event.target;

setFullName((prevValue) => {
if (name === "fName") {
return {
fName: value,
lName: prevValue.lName
};
} else if (name === "lName") {
return {
fName: prevValue.fName,
lName: value
};
}
});
}

return (
<div>
<form className="container">
<h1>
Hello {fullName.fName} {fullName.lName}
</h1>
<input
name="fName"
onChange={updateFullName}
placeholder="First Name"
value={fullName.fName}
></input>
<input
name="lName"
onChange={updateFullName}
placeholder="Last Name"
value={fullName.lName}
></input>
<button type="submit">Submit</button>
</form>
</div>
);
}

export default App;

 

 


이제는 이메일까지 넣어서 다음과 같이 나오게 해보자. 

 

 

 

아직 value를 왜 써줘야하는지 모르겠음. 안적어줘도 잘 돌아가는데..

 

 

import React, { useState } from "react";
import "./App.css";

function App() {
const [fullName, setFullName] = useState({
fName: "",
lName: "",
email: ""
});

function updateFullName(event) {
const { value, name } = event.target;

setFullName((prevValue) => {
if (name === "fName") {
return {
fName: value,
lName: prevValue.lName,
email: prevValue.email
};
} else if (name === "lName") {
return {
fName: prevValue.fName,
lName: value,
email: prevValue.email
};
} else if (name === "email") {
return {
fName: prevValue.fName,
lName: prevValue.lName,
email: value
};
}
});
}

return (
<div className="container">
<h1>
Hello {fullName.fName} {fullName.lName}
</h1>
<p>{fullName.email}</p>
<input
onChange={updateFullName}
name="fName"
type="text"
placeholder="First Name"
value={fullName.fName}
/>
<input
onChange={updateFullName}
name="lName"
type="text"
placeholder="Last Name"
value={fullName.lName}
/>
<input
onChange={updateFullName}
name="email"
type="text"
placeholder="email"
value={fullName.email}
/>
<button>Submit</button>
</div>
);
}

export default App;

 

 

 

CSS 

* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-weight: 300;
}

body {
font-family: "Source Sans Pro", sans-serif;
color: white;
font-weight: 300;
background: #50a3a2;
width: 100%;
height: 100%;
}

.container {
margin: 10% auto;
padding: 80px 0;
text-align: center;
}

.container h1 {
font-size: 40px;
font-weight: 200;
}

input {
border: 1px solid rgba(252, 252, 252, 0.4);
background-color: rgba(252, 252, 252, 0.2);
width: 250px;
border-radius: 3px;
font-family: "Source Sans Pro", sans-serif;
padding: 10px 15px;
margin: 0 auto 10px auto;
display: block;
text-align: center;
font-size: 18px;
color: white;
font-weight: 300;
}

button {
appearance: none;
outline: 0;
background-color: white;
border: 0;
padding: 10px 15px;
color: #50a3a2;
border-radius: 3px;
width: 250px;
font-size: 18px;
}

댓글