state (์ํ)๋ ์ปดํฌ๋ํธ๊ฐ ๊ฐ์ง ์ ์๋ ๋ณ๊ฒฝ ๊ฐ๋ฅํ ๋ฐ์ดํฐ๋ค.
๋ฆฌ์กํธ 16.8 ์ด์ ์๋ ์ปดํฌ๋ํธ๊ฐ ์ํ๋ฅผ ๊ฐ๊ธฐ ์ํด์ ํด๋์คํ ์ปดํฌ๋ํธ๋ง ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค.
ํ์ง๋ง 16.8 ์ดํ๋ถํฐ hook์ ์ด์ฉํด์ ํจ์ํ ์ปดํฌ๋ํธ๋ ์ํ๋ฅผ ๊ฐ์ง ์ ์๊ฒ ๋์๋ค.
๊ทธ hook์ด ๋ฐ๋ก useState ๋ค.
useState๋ ์ํ์ ๊ทธ ์ํ๋ฅผ ๋ณ๊ฒฝ์ํฌ ์ ์๋ setํจ์๋ฅผ ๋ฆฌํดํ๋ค.
useState์ ์ธ์๋ก๋ ์ํ์ ์ด๊ธฐ๊ฐ์ด ๋ค์ด๊ฐ๋ค.
๊ธฐ๋ณธ์ ์ธ ๋ฌธ๋ฒ์ ๋ค์๊ณผ ๊ฐ๋ค.
import React, {useState} from 'react'
const Counter = () => {
const [count, setCount] = useState(0);
return (
<h1>Count : {count}</h1>
)
}
export default Counter;
setState๋ useState์ ๋ฆฌํด๊ฐ์ผ๋ก state๋ฅผ ์ ๋ฐ์ดํธํ ๋ ์ฌ์ฉํ๋ ํจ์์ด๋ค.
์ด๋, ๋ณดํต setState์์ state๋ฅผ ์ง์ ์ ์ผ๋ก ์ฌ์ฉํ์ฌ ์ ๋ฐ์ดํธ๋ฅผ ํ๋ ๊ฒฝ์ฐ๊ฐ ์๋ค.
์ด๋ ํ๋ฆฐ ๋ฐฉ์์ ์๋์ง๋ง ์ด๋ค ์ํฉ์์๋ ์๋์ ๋ค๋ฅด๊ฒ state๋ฅผ ์ ๋ฐ์ดํธ ์ํฌ ์ ์๋ค.
์๋ฅผ ํ๋ฒ ๋ณด์!
์์์์ Counter์ด๋ผ๋ ์ปดํฌ๋ํธ๋ count์ randomNum์ด๋ผ๋ state๋ฅผ ๋๊ฐ ๊ฐ์ง๊ณ ์๋ค.
๋ฒํผ์ ํด๋ฆญํ๋ฉด count๋ 1์ฉ ์ฆ๊ฐํ๊ณ ๋์์ randomNum์๋ ๋๋คํ ์๋ก์ด ์ซ์๊ฐ ๋ค์ด๊ฐ๋ค.
์ฌ๊ธฐ์ ์ฃผ์ ๊น๊ฒ ๋ด์ผํ๋ ๊ฒ์ handleClick์ด๋ผ๋ ํจ์์์ setCount, setRandomNum์ ๊ฐ์ด ์ผ๋ค๋ ๊ฒ์ด๋ค.
set ํจ์๋ฅผ ์ฐ๊ฒ ๋๋ฉด state ๊ฐ์ด ๋ณ๊ฒฝ๋๊ธฐ ๋๋ฌธ์ ๋ฆฌ์กํธ์์ ์ปดํฌ๋ํธ๋ฅผ ์๋์ผ๋ก ๋ฆฌ๋ ๋๋ง ์ํจ๋ค.
๋ฐ๋ผ์ setํจ์๋ฅผ ๋๊ฐ ์ฐ๊ฒ ๋๋ฉด ๋ฆฌ๋ ๋๋ง์ด ๋๋ฒ ์ผ์ด๋ ๊ฒ์ด๋ผ๊ณ ์์ํ ์ ์๋ค.
ํ์ง๋ง ๋ฆฌ์กํธ๋ ๋๋ํ๊ฒ๋ batching์ด๋ผ๋ ๊ฒ์ ์ด์ฉํด์ ์ฌ๋ฌ๊ฐ์ setState ํจ์๋ฅผ ํ๋์ ๋ฆฌ๋ ๋๋ง์ผ๋ก ์ฒ๋ฆฌํ ์ ์๋ค.
๊ทธ๋์ console.log(โrenderโ)์ ํตํด์ ๋ฆฌ๋ ๋๋ง ํ์๋ฅผ ๋ณด๋ฉด ๋ฒํผ์ด ํด๋ฆญ๋ ๋๋ง๋ค ์ฝ์์ฐฝ์ render๊ฐ ํ๋ฒ๋ง ์ฆ๊ฐํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
(๊ธฐ๋ณธ์ ์ผ๋ก 2์ฉ ์ฆ๊ฐํ๊ฒ ๋๋ค๋ฉด ๊ทธ๊ฒ์ React.StrictMode๋ก ์ธํด ์๋์ผ๋ก ์ปดํฌ๋ํธ๊ฐ ๋๋ธ ๋ ๋๋ง๋๋๋ก ์ค์ ๋์ด์๊ธฐ ๋๋ฌธ์ด๋ค. ๋ง์ฝ index.js์์ React.StrictMode๋ฅผ ์ ๊ฑฐํ๊ฒ ๋๋ฉด ํ๋ฒ์ฉ ๋ฆฌ๋ ๋๋ง ๋๋ค.)
import React, { useState } from "react";
const Counter = () => {
const [count, setCount] = useState(0);
const [randomNum, setRandomNum] = useState(0);
const handleClick = () => {
setCount(count + 1);
setRandomNum(Math.floor(Math.random() * 10));
};
console.log('render')
return (
<div>
<h2>Count : {count}</h2>
<h2>Random Num : {randomNum}</h2>
<button onClick={handleClick}>plus & new random</button>
</div>
);
};
export default Counter;
ํ์ง๋ง, ์ ๊ธฐํ๊ฒ ์ด batching ๋๋ฌธ์ ์๋ก ๋ค๋ฅธ setํจ์์ ๊ฒฝ์ฐ state๊ฐ ์ ์ ๋ฐ์ดํธ ๋์ง๋ง ๊ฐ์ setํจ์๋ฅผ ์ฌ๋ฌ๋ฒ ํธ์ถํ๋ ๊ฒฝ์ฐ์๋ ์๋ํ ๋ฐ์ ๋ค๋ฅด๊ฒ state๊ฐ ์ ๋ฐ์ดํธ๋์ง ์๋ ํ์์ด ์๊ธด๋ค.
์์ ์์๋ฅผ ์กฐ๊ธ๋ง ๋ณํํด๋ณด์.
๋ค์๊ณผ ๊ฐ์ด setCount๋ฅผ ํ๋ฒ์ด ์๋ 3๋ฒ์ ํธ์ถํ๊ณ setRandomNum์ ํ๋ฒ ํธ์ถํ๋ฉด ์ด๋ค ๊ฒฐ๊ณผ๊ฐ ๋์ฌ์ง ํ๋ฒ ์์์ ํด๋ณด์.
์๋ ์๋๋๋ก๋ผ๋ฉด count๊ฐ 1์ฉ์ด ์๋ 3์ฉ ์ฆ๊ฐํด์ผ ํ๋ค. ํ์ง๋ง ๋ฒํผ์ ํด๋ฆญํด ๋ณด๋ฉด 1์ฉ ์ฆ๊ฐํจ์ ์ ์ ์๋ค.
์ด๋ ์์์ ์ค๋ช ํ batching ๋๋ฌธ์ด๋ค. batching์ ์ฌ๋ฌ๊ฐ์ setState ํจ์๋ฅผ ํ๋๋ก ๋ฌถ์ด์ ๋ฆฌ๋ ๋๋ง์ ํ๋ฒ๋ง ํ๋ค๊ณ ํ๋ค.
๋ฐ๋ผ์ setCount๊ฐ ์ฌ๋ฌ๋ฒ ํธ์ถ๋์๋ค๊ณ ํด๋ ์ด๋ฅผ ๋ฌถ์ด์ ๋ฆฌ๋ ๋๋ง์ด ํ๋ฒ๋ง ์ผ์ด๋๋ ๊ฒ์ด๋ค.
๋ฆฌ๋ ๋๋ง์ด ํ๋ฒ๋ง ์ผ์ด๋๊ธฐ ๋๋ฌธ์ 3๊ฐ์ setCount์์ count๊ฐ ๋ชจ๋ ๊ฐ์ ๊ฐ์ด์ฌ์ 1๋ง ์ฆ๊ฐ๋๋ ๊ฒ์ด๋ค.
๊ทธ๋ ๋ค๋ฉด ์ฐ๋ฆฌ๊ฐ ์๋ํ ๋๋ก count๊ฐ 3์ฉ ์ฆ๊ฐํ ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น?
count+3์ด์ด๋ ๋์ง๋ง setCount๋ฅผ ์ง๊ธ์ฒ๋ผ 3๋ฒ ์ฌ์ฉํด๋ ๋๋ค.
ํ์ง๋ง ์ด๋, count๋ฅผ ์ฐ๋ ๊ฒ์ด ์๋ ์ฝ๋ฐฑ์ ์ฌ์ฉํด์ prev ๊ฐ์ ์ฌ์ฉํด์ผํ๋ค.
prev๋ setํจ์์์ ์ ๊ณตํ๋ ์ด์ ๊ฐ์ด๋ค. prev๋ ์ด์ ๊ฐ์ ์ ์ฅํ๋ ์์๊ณต๊ฐ์ด๊ธฐ ๋๋ฌธ์ ๋ฆฌ๋ ๋๋ง ์์ด๋ ์ ๋ฐ์ดํธ๋ ๊ฐ์ด ์ ์ฅ๋๋ค.
import React, { useState } from "react";
const Counter = () => {
const [count, setCount] = useState(0);
const [randomNum, setRandomNum] = useState(0);
const handleClick = () => {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
setRandomNum(Math.floor(Math.random() * 10));
};
console.log('render')
return (
<div>
<h2>Count : {count}</h2>
<h2>Random Num : {randomNum}</h2>
<button onClick={handleClick}>plus & new random</button>
</div>
);
};
export default Counter;
๋ฐ๋ผ์ ์ฒ์์ count+1 ์ด ๋์ด 1์ด prev ๊ฐ์ผ๋ก ์ ์ฅ๋๋ฉฐ ๋ค์ setCount ํจ์์์ ์ด prev๊ฐ์ ์ด์ฉํ์ฌ ๋ค์ 1์ ์ฆ๊ฐ์ํด์ผ๋ก์จ prev ๊ฐ์ด 2๊ฐ ๋๋ค. ๋งจ๋ง์ง๋ง setCount์์ prev๋ 2๊ฐ ๋๊ณ ์ด๋ฅผ 1 ์ฆ๊ฐ์์ผ ์ต์ข ์ ์ผ๋ก 3์ด ๋๋ค.
์ด๋ ๊ฒ prev ๊ฐ์ ์ ๋ฐ์ดํธ ํ๋ ๊ฒฝ์ฐ์๋ state๋ฅผ ์ง์ ์ ์ผ๋ก ์ฌ์ฉํ์ฌ ๋ณ๊ฒฝํ๊ธฐ ๋ณด๋ค๋ prev๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
const handleClick = () => {
setCount(prev => prev + 1); // 0 => 1
setCount(prev => prev + 1); // 1 => 2
setCount(prev => prev + 1); // 2 => 3
setRandomNum(Math.floor(Math.random() * 10));
};
๋ฐฐ์ด์ด๋ ๊ฐ์ฒด์ ๊ฒฝ์ฐ ๋ฐ๋์ ๋ถ๋ณ์ฑ์ ์ง์ผ์ค์ผํ๋ค.
๋ฐ๋ผ์ ๋ฐฐ์ด์ ๊ฒฝ์ฐ์๋ push, splice, sort๊ณผ ๊ฐ์ด ๋ฐฐ์ด์ ์ง์ ์ ์ผ๋ก ๋ณ๊ฒฝํ๋ ๋ฉ์๋๋ ์ฌ์ฉํ ์ ์๋ค. ๋ง์ฝ ์ฌ์ฉํ๊ณ ์ถ์ผ๋ฉด ๊ธฐ์กด์ ๋ฐฐ์ด์ ๋ณต์ฌํด์ ๋ณต์ฌํ ๋ฐฐ์ด์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
๊ฐ์ฒด์ ๊ฒฝ์ฐ์๋ ๋ฐฐ์ด๊ณผ ๋ง์ฐฌ๊ฐ์ง๊ณ ๋ถ๋ณ์ฑ์ ์ง์ผ์ค์ผ ํ๋ค. ์ด๋ ์ฃผ์ํ ๊ฒ์ class component์ setํจ์๋ ๊ธฐ์กด์ object๋ฅผ spread operator๋ก destructure ํ์ง ์์๋ ์๋์ ์ผ๋ก ๊ธฐ์กด์ object์ ๋ณ๊ฒฝ์ฌํญ์ ํฉ์ณ์ฃผ์๋ค. ํ์ง๋ง useState์ hook์ ์๋์ ์ผ๋ก ์ด์ ๊ฐ์ฒด์ ํ์ฌ ๊ฐ์ฒด๋ฅผ ํฉ์ณ์ฃผ์ง ์๊ธฐ ๋๋ฌธ์ spread operator๋ก destructuring ํ ํ์ ์๋ก์ด value๋ฅผ key๋ฅผ ํตํด์ ์ ๋ฐ์ดํธ ํด์ค์ผํ๋ค. ๋ฆฌ์กํธ ๊ณต์๋ฌธ์์ ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์จ์ ธ์๋ค.
๐ก < **์ฃผ์ >**ํด๋์ค ์ปดํฌ๋ํธ์ย setState
ย ๋ฉ์๋์๋ ๋ค๋ฅด๊ฒ,ย useState
๋ ๊ฐฑ์ ๊ฐ์ฒด(update objects)๋ฅผ ์๋์ผ๋ก ํฉ์น์ง๋ ์์ต๋๋ค. ํจ์ ์
๋ฐ์ดํฐ ํผ์ ๊ฐ์ฒด ์ ๊ฐ ์ฐ์ฐ์์ ๊ฒฐํฉํจ์ผ๋ก์จ ์ด ๋์์ ๋ณต์ ํ ์ ์์ต๋๋ค.
const [state, setState] = useState({});
setState(prevState => {
// Object.assign would also work
return {...prevState, ...updatedValues};
});
๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก๋ย useReducer
๊ฐ ์๋๋ฐ ์ด๋ ์ฌ๋ฌ๊ฐ์ ํ์๊ฐ๋ค์ ํฌํจํ state ๊ฐ์ฒด๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐ์ ๋ ์ ํฉํฉ๋๋ค.
์ฃผ๋ก, ์ด๋ฅผ ์ํด์ spread operator๋ฅผ ์ฌ์ฉํ๋ค.
spread operator๋ ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ๋ฉด ๋๋ค.
// ๊ฐ์ฒด์์ ํ์ง ๋ง์์ผํ ๊ฒ : ๋ณ๊ฒฝํ key:value ๊ฐ๋ง ๋ฃ๋ ๊ฒ
setState({name:value})
// ๊ฐ์ฒด์ ๊ฒฝ์ฐ
setState(prev => {
return {
...prev,
[name] : value
}
}
// ๋ฐฐ์ด์ ๊ฒฝ์ฐ
setState(prev => [...prev, newValue])
๋ค์์ ์์๋ ์ด๋ฆ, ์ด๋ฉ์ผ, ๋์ด๋ฅผ ์ ๋ ฅํ๋ UserInfo ๋ถ๋ถ๊ณผ tag๋ค์ ์ ๋ ฅํ๋ AddTag ๋ถ๋ถ์ด ์๋ค.
๊ทธ๋ฆฌ๊ณ ๊ทธ ์๋์ ์ด๊ฒ์ ์ถ๋ ฅํ๋ ๋ถ๋ถ์ด ์๋ค.
input์ ๊ฐ์ ๊ด๋ฆฌํ๋ userInfo state๊ฐ ํ๋์๊ณ ์ ๋ ฅํ๊ณ ์๋ currentTag state ํ๊ฐ์, tag๋ค์ ์ ์ฒด ๊ด๋ฆฌํ๋ tags๋ผ๋ state ํ๋๊ฐ ์๋ค.
userInfo์ ๊ฐ์ ๊ฐ์ฒด๋ setState๋ฅผ ์ด์ฉํด์ value๊ฐ์ ์
๋ฐ์ดํธํ ๋ key๊ฐ ๋์ ์ด๋ฏ๋ก [key]
ํ์์ผ๋ก ์์ฑ์ ํด์ผํ๋ค.
import React, { useState } from "react";
const User = () => {
const [userInfo, setUserInfo] = useState({
email: "",
name: "",
age: 0
});
const [currentTag, setCurrentTag] = useState("");
const [tags, setTags] = useState([]);
const handleUserInfoChange = (e) => {
const { name, value } = e.target;
setUserInfo((prev) => {
return {
...userInfo,
[name]: value
};
});
};
const handleTagChange = (e) => {
setCurrentTag(e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
setTags((prev) => [...prev, currentTag]);
setCurrentTag("");
};
console.log("render");
return (
<div>
<h2>User Info</h2>
<input
type="text"
name="name"
value={userInfo["name"]}
placeholder="์ด๋ฆ"
onChange={handleUserInfoChange}
/>
<input
type="text"
name="email"
value={userInfo["email"]}
placeholder="์ด๋ฉ์ผ"
onChange={handleUserInfoChange}
/>
<input
type="number"
name="age"
value={userInfo["age"]}
placeholder="๋์ด"
onChange={handleUserInfoChange}
/>
<form onSubmit={handleSubmit}>
<h2>Add Tag</h2>
<input
type="text"
name="tags"
value={currentTag}
onChange={handleTagChange}
/>
<button type="button">์ ์ถ</button>
</form>
<h2>Print User & Tag</h2>
<h3>-User</h3>
{Object.keys(userInfo).map((key) => (
<p key={key}>
{key} : {userInfo[key]}
</p>
))}
<h3>-Tag</h3>
{tags.map((tag, idx) => (
<p key={idx}>{tag}</p>
))}
</div>
);
};
export default User;
์ ๋ง ๊ผผ๊ผผํ๊ฒ ์ ๋ฆฌํด์ฃผ์ จ๋ค์ ๊ฐ์ฌํฉ๋๋ค :)