import React from "react";
import { useState } from "react";
function App() {
const [number, setNumber] = useState(0);
const increase = () => {
setNumber(number + 1);
};
const increaseAsync = () => {
setTimeout(() => {
setNumber(number + 1);
}, 2000);
};
return (
<div>
<button onClick={increase}>increase</button>
<button onClick={increaseAsync}>increaseAsync</button>
<h1>{number}</h1>
</div>
);
}
export default App;
우리는 useState를 통해 상태를 만들어 동적으로 상태를 관리한다.
위 코드에서 increase함수를 만들었고 버튼에 함수를 달아놓았다.
number가 set함수를통해 +1 씩 증가할 것이다.
하지만 increaseAsync 비동기 증가함수는 어떻게 될까?
버튼을 두 번 클릭해 number의 값이 2로 되게끔 만든다.
현재 number는 2이고 비동기증가 함수를 누르면 2초후에 1이 늘어난다.
2초가 지나기전에 5번을 더 누르면 어떻게 될까?
number은 7이되고 2초가 지난시점에 갑자기 3으로 돌아온다.
왜 이런 예측 불가능한 상황이 이루어질까? 내부적으로 어떻게 동작하길래?
비동기로 증가하는 함수는 눌렀을때 이전 스테이트의 값을 기억하고 있다.
그렇기때문에 2초가 지나기전에 7을 만들더라도 비동기함수의 입장에선 2를 보고있었기때문에 +1을
해주어 자신의 역할을 다 한것이다.
그렇다면 위 코드를 어떻게 개선 할 수 있을까?
import React from "react";
import { useState } from "react";
function App() {
const [number, setNumber] = useState(0);
const increase = () => {
setNumber(number + 1);
};
const increaseAsync = () => {
setTimeout(() => {
setNumber((currentNumber)=>currentNumber+1);
}, 2000);
};
return (
<div>
<button onClick={increase}>increase</button>
<button onClick={increaseAsync}>increaseAsync</button>
<h1>{number}</h1>
</div>
);
}
export default App;
위와같이 비동기 코드에서 set함수안에 함수 자체를 전달해서 개선하는 방법이 있다.
import React from "react";
import { useState } from "react";
function App() {
const [inputValue, setInputValue] = usesState("");
const [user, setUser] = useState({
name: "mimamo",
email: "robo888@naver.com",
images: ["profile.png", "cover.png"],
});
const userNameChangeFun = () => {
setUser((user.name = inputValue));
};
console.log(user);
return (
<div>
<h2>User:</h2>
<input
onChange={(e) => setInputValue(e.target.value)}
placeholder="write there!"
></input>
<button onClick={userNameChangeFun}>UserNameChange</button>
<span>user name : {user.name}</span>
</div>
);
}
export default App;
위와 같이 user객체를 내가 적은 인풋값으로 대체할때는 어떻게 해야할까?
위 코드가 과연 해결책일까? 아니다
배열 또는 객체의 자료구조 형태는 레퍼런트 타입이기 때문에 즉 안에 내용이 바뀌어도 useState는 데이터를 감싼 [] , {} 이 껍데기들이 바뀌지 않는 한 업데이트 되지 않는다.
간단하게 콘솔에 const a = [] const b = [] a===b 를 찍어보면 false가 나오는 이유에 대해 생각해보면 받아들이기 쉬울것이다.
그렇기 때문에 위에 코드를 개선해보면
import React from "react";
import { useState } from "react";
function App() {
const [inputValue, setInputValue] = usesState("");
const [user, setUser] = useState({
name: "mimamo",
email: "robo888@naver.com",
images: ["profile.png", "cover.png"],
});
const userNameChangeFun = () => {
setUser((prev) => ({ ...prev, name: inputValue }));
};
console.log(user);
return (
<div>
<h2>User:</h2>
<input
onChange={(e) => setInputValue(e.target.value)}
placeholder="write there!"
></input>
<button onClick={userNameChangeFun}>UserNameChange</button>
<span>user name : {user.name}</span>
</div>
);
}
export default App;
spread operator를 사용하여 위처럼 setUser 함수에 이름을 오버라이딩해 업데이팅 시켜야 useState는 껍데기가 바뀌었네? 라고 판단해 업데이트를 해준다.
Fe개발자가 하는 역할중에 중요한 개념이다.
입력양식을 받아 만약 name : userName : address : email :city : country : 이런식으로 많은 인풋창을 구현하고 하나하나 useState로 인풋을 관리하기엔 가독성과 효율이 너무나도 크게 떨어질것이다.
import React from "react";
import { useState } from "react";
function App() {
const [user, setUser] = useState({
name: "",
surname: "",
username: "",
email: "",
password: "",
country: "",
city: "",
address: "",
});
const handleChange = (e) => {
setUser((prev) => ({ ...prev, [e.target.name]: e.target.value }));
};
return (
<div>
<input onChange={handleChange} name="name" placeholder="name"></input>
<input
onChange={handleChange}
name="surname"
placeholder="surname"
></input>
<input
onChange={handleChange}
name="username"
placeholder="username"
></input>
<input onChange={handleChange} name="email" placeholder="email"></input>
<input
onChange={handleChange}
name="password"
placeholder="password"
></input>
<input
onChange={handleChange}
name="country"
placeholder="country"
></input>
<input onChange={handleChange} name="city" placeholder="city"></input>
<input
onChange={handleChange}
name="address"
placeholder="address"
></input>
<button>submit</button>
</div>
);
}
export default App;
위와같은 방식은 한번에 여러 인풋들을 모두 컨트롤 할 수 있게된다.
어떻게 되는것일까?
해답은 인풋속성의 name에있다 . 우리는 그 name을 지정하고 이용해
[e.target.name]이란 대괄호 표기법으로 name의 키값에 접근하고 벨류를 우리가 원하는 value로 설정하는 것이다.
오늘은 useState의 대해 딥하게 알아보았다. 모두 코드를 조금 집중적으로 보고 특징과 포인트를 잘 기억하면 좋겠다.