title |
---|
useState |
useState
๋ ์ปดํฌ๋ํธ์ state ๋ณ์๋ฅผ ์ถ๊ฐํ ์ ์๋ React Hook์
๋๋ค.
const [state, setState] = useState(initialState)
์ปดํฌ๋ํธ์ ์ต์์ ๋ ๋ฒจ์์ useState
๋ฅผ ํธ์ถํ์ฌ state ๋ณ์๋ฅผ ์ ์ธํฉ๋๋ค.
import { useState } from 'react';
function MyComponent() {
const [age, setAge] = useState(28);
const [name, setName] = useState('Taylor');
const [todos, setTodos] = useState(() => createTodos());
// ...
๋ฐฐ์ด ๊ตฌ์กฐ ๋ถํด๋ฅผ ์ฌ์ฉํ์ฌ [something, setSomething]
๊ณผ ๊ฐ์ state ๋ณ์์ ์ด๋ฆ์ ์ง์ ํ๋ ๊ฒ์ด ๊ท์น์
๋๋ค.
์๋์์ ๋ ๋ง์ ์์๋ฅผ ํ์ธํ์ธ์.
initialState
: state์ ์ด๊ธฐ ์ค์ ๊ฐ์ ๋๋ค. ์ด๋ค ์ ํ์ ๊ฐ์ด๋ ์ง์ ํ ์ ์์ง๋ง ํจ์์ ๋ํด์๋ ํน๋ณํ ๋์์ด ์์ต๋๋ค. ์ด ์ธ์๋ ์ด๊ธฐ ๋ ๋๋ง ์ดํ์๋ ๋ฌด์๋ฉ๋๋ค.- ํจ์๋ฅผ
initialState
๋ก ์ ๋ฌํ๋ฉด ์ด๋ฅผ ์ด๊ธฐํ ํจ์๋ก ์ทจ๊ธํฉ๋๋ค. ์ด ํจ์๋ ์์ํด์ผ ํ๊ณ ์ธ์๋ฅผ ๋ฐ์ง ์์์ผ ํ๋ฉฐ ๋ฐ๋์ ์ด๋ค ๊ฐ์ ๋ฐํํด์ผ ํฉ๋๋ค. React๋ ์ปดํฌ๋ํธ๋ฅผ ์ด๊ธฐํํ ๋ ์ด๊ธฐํ ํจ์๋ฅผ ํธ์ถํ๊ณ , ๊ทธ ๋ฐํ๊ฐ์ ์ด๊ธฐ state๋ก ์ ์ฅํฉ๋๋ค. ์๋ ์์๋ฅผ ์ฐธ๊ณ ํ์ธ์.
- ํจ์๋ฅผ
useState
๋ ์ ํํ ๋ ๊ฐ์ ๊ฐ์ ๊ฐ์ง ๋ฐฐ์ด์ ๋ฐํํฉ๋๋ค.
- ํ์ฌ state์
๋๋ค. ์ฒซ ๋ฒ์งธ ๋ ๋๋ง ์ค์๋ ์ ๋ฌํ
initialState
์ ์ผ์นํฉ๋๋ค. - state๋ฅผ ๋ค๋ฅธ ๊ฐ์ผ๋ก ์
๋ฐ์ดํธํ๊ณ ๋ฆฌ๋ ๋๋ง์ ์ด๋ฐํ ์ ์๋
set
ํจ์์ ๋๋ค.
useState
๋ Hook์ด๋ฏ๋ก ์ปดํฌ๋ํธ์ ์ต์์ ๋ ๋ฒจ์ด๋ ์ง์ ๋ง๋ Hook์์๋ง ํธ์ถํ ์ ์์ต๋๋ค. ๋ฐ๋ณต๋ฌธ์ด๋ ์กฐ๊ฑด๋ฌธ ์์์๋ ํธ์ถํ ์ ์์ต๋๋ค. ํ์ํ ๊ฒฝ์ฐ ์ ์ปดํฌ๋ํธ๋ฅผ ์ถ์ถํ๊ณ state๋ฅผ ๊ทธ ์์ผ๋ก ์ฎ๊ธฐ์ธ์.- Strict Mode์์ React๋ ์๋์น ์์ ๋ถ์๋ฌผ์ ์ฐพ๊ธฐ ์ํด ์ด๊ธฐํ ํจ์๋ฅผ ๋ ๋ฒ ํธ์ถํฉ๋๋ค. ์ด๋ ๊ฐ๋ฐ ํ๊ฒฝ ์ ์ฉ ๋์์ด๋ฉฐ ํ๋ก๋์ ํ๊ฒฝ์๋ ์ํฅ์ ๋ฏธ์น์ง ์์ต๋๋ค. ์ด๊ธฐํ ํจ์๊ฐ ์์ํ๋ค๋ฉด(๊ทธ๋์ผ ํฉ๋๋ค) ๋์์ ์ํฅ์ ๋ฏธ์น์ง ์์ต๋๋ค. ํธ์ถ ์ค ํ๋์ ๊ฒฐ๊ณผ๋ ๋ฌด์๋ฉ๋๋ค.
useState
๊ฐ ๋ฐํํ๋ set
ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด state๋ฅผ ๋ค๋ฅธ ๊ฐ์ผ๋ก ์
๋ฐ์ดํธํ๊ณ ๋ฆฌ๋ ๋๋ง์ ์ด๋ฐํ ์ ์์ต๋๋ค. ์ฌ๊ธฐ์๋ ๋ค์ state๋ฅผ ์ง์ ์ ๋ฌํ๊ฑฐ๋, ์ด์ state๋ก๋ถํฐ ๊ณ์ฐํ ํจ์๋ฅผ ์ ๋ฌํ ์๋ ์์ต๋๋ค.
const [name, setName] = useState('Edward');
function handleClick() {
setName('Taylor');
setAge(a => a + 1);
// ...
nextState
: state๊ฐ ๋ ๊ฐ์ ๋๋ค. ๊ฐ์ ๋ชจ๋ ๋ฐ์ดํฐ ํ์ ์ด ํ์ฉ๋์ง๋ง, ํจ์์ ๋ํด์๋ ํน๋ณํ ๋์์ด ์์ต๋๋ค.- ํจ์๋ฅผ
nextState
๋ก ์ ๋ฌํ๋ฉด ์ ๋ฐ์ดํฐ ํจ์๋ก ์ทจ๊ธํฉ๋๋ค. ์ด ํจ์๋ ์์ํด์ผ ํ๊ณ , ๋๊ธฐ ์ค์ธ state๋ฅผ ์ ์ผํ ์ธ์๋ก ์ฌ์ฉํด์ผ ํ๋ฉฐ, ๋ค์ state๋ฅผ ๋ฐํํด์ผ ํฉ๋๋ค. React๋ ์ ๋ฐ์ดํฐ ํจ์๋ฅผ ๋๊ธฐ์ด์ ๋ฃ๊ณ ์ปดํฌ๋ํธ๋ฅผ ๋ฆฌ๋ ๋๋ง ํฉ๋๋ค. ๋ค์ ๋ ๋๋ง ์ค์ React๋ ๋๊ธฐ์ด์ ์๋ ๋ชจ๋ ์ ๋ฐ์ดํฐ๋ฅผ ์ด์ state์ ์ ์ฉํ์ฌ ๋ค์ state๋ฅผ ๊ณ์ฐํฉ๋๋ค. ์๋ ์์๋ฅผ ์ฐธ๊ณ ํ์ธ์.
- ํจ์๋ฅผ
set
ํจ์๋ ๋ฐํ๊ฐ์ด ์์ต๋๋ค.
-
set
ํจ์๋ ๋ค์ ๋ ๋๋ง์ ๋ํ state ๋ณ์๋ง ์ ๋ฐ์ดํธํฉ๋๋ค.set
ํจ์๋ฅผ ํธ์ถํ ํ์๋ state ๋ณ์์๋ ์ฌ์ ํ ํธ์ถ ์ ํ๋ฉด์ ์๋ ์ด์ ๊ฐ์ด ๋ด๊ฒจ ์์ต๋๋ค. -
์ฌ์ฉ์๊ฐ ์ ๊ณตํ ์๋ก์ด ๊ฐ์ด
Object.is
์ ์ํด ํ์ฌstate
์ ๋์ผํ๋ค๊ณ ํ์ ๋๋ฉด, React๋ ์ปดํฌ๋ํธ์ ๊ทธ ์์๋ค์ ๋ฆฌ๋ ๋๋งํ์ง ์์ต๋๋ค. ์ด๊ฒ์ด ๋ฐ๋ก ์ต์ ํ์ ๋๋ค. ๊ฒฝ์ฐ์ ๋ฐ๋ผ React๊ฐ ์์์ ๊ฑด๋๋ฐ๊ธฐ ์ ์ ์ปดํฌ๋ํธ๋ฅผ ํธ์ถํด์ผ ํ ์๋ ์์ง๋ง, ์ฝ๋์ ์ํฅ์ ๋ฏธ์น์ง๋ ์์ต๋๋ค. -
React๋ state ์ ๋ฐ์ดํธ๋ฅผ batch ํฉ๋๋ค. ๋ชจ๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ๊ฐ ์คํ๋๊ณ
set
ํจ์๋ฅผ ํธ์ถํ ํ์ ํ๋ฉด์ ์ ๋ฐ์ดํธํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ๋จ์ผ ์ด๋ฒคํธ ์ค์ ์ฌ๋ฌ ๋ฒ ๋ฆฌ๋ ๋๋ง ํ๋ ๊ฒ์ ๋ฐฉ์งํ ์ ์์ต๋๋ค. ๋๋ฌผ์ง๋ง DOM์ ์ ๊ทผํ๊ธฐ ์ํด React๊ฐ ํ๋ฉด์ ๋ ์ผ์ฐ ์ ๋ฐ์ดํธํ๋๋ก ๊ฐ์ ํด์ผ ํ๋ ๊ฒฝ์ฐ,flushSync
๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. -
๋ ๋๋ง ๋์ค
set
ํจ์๋ฅผ ํธ์ถํ๋ ๊ฒ์ ํ์ฌ ๋ ๋๋ง ์ค์ธ ์ปดํฌ๋ํธ ๋ด์์๋ง ํ์ฉ๋ฉ๋๋ค. React๋ ํด๋น ์ถ๋ ฅ์ ๋ฒ๋ฆฌ๊ณ ์ฆ์ ์๋ก์ด state๋ก ๋ค์ ๋ ๋๋ง์ ์๋ํฉ๋๋ค. ์ด ํจํด์ ๊ฑฐ์ ํ์ํ์ง ์์ง๋ง ์ด์ ๋ ๋๋ง์ ์ ๋ณด๋ฅผ ์ ์ฅํ๋ ๋ฐ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์๋ ์์๋ฅผ ์ฐธ๊ณ ํ์ธ์. -
Strict Mode์์ React๋ ์๋์น์์ ๋ถ์๋ฌผ์ ์ฐพ๊ธฐ ์ํด ์ ๋ฐ์ดํฐ ํจ์๋ฅผ ๋ ๋ฒ ํธ์ถํฉ๋๋ค. ์ด๋ ๊ฐ๋ฐ ํ๊ฒฝ ์ ์ฉ ๋์์ด๋ฉฐ ํ๋ก๋์ ํ๊ฒฝ์๋ ์ํฅ์ ๋ฏธ์น์ง ์์ต๋๋ค. ๋ง์ฝ ์ ๋ฐ์ดํฐ ํจ์๊ฐ ์์ํ๋ค๋ฉด(๊ทธ๋์ผ ํฉ๋๋ค) ๋์์ ์ํฅ์ ๋ฏธ์น์ง ์์ต๋๋ค. ํธ์ถ ์ค ํ๋์ ๊ฒฐ๊ณผ๋ ๋ฌด์๋ฉ๋๋ค.
์ปดํฌ๋ํธ์ ์ต์์ ๋ ๋ฒจ์์ useState
๋ฅผ ํธ์ถํ์ฌ ํ๋ ์ด์์ state ๋ณ์๋ฅผ ์ ์ธํ์ธ์.
import { useState } from 'react';
function MyComponent() {
const [age, setAge] = useState(42);
const [name, setName] = useState('Taylor');
// ...
๋ฐฐ์ด ๊ตฌ์กฐ ๋ถํด๋ฅผ ์ฌ์ฉํ์ฌ [something, setSomething]
๊ณผ ๊ฐ์ state ๋ณ์์ ์ด๋ฆ์ ์ง์ ํ๋ ๊ฒ์ด ๊ด๋ก์
๋๋ค.
useState
๋ ์ ํํ ๋ ๊ฐ์ ํญ๋ชฉ์ด ์๋ ๋ฐฐ์ด์ ๋ฐํํฉ๋๋ค.
- ์ด state ๋ณ์์ ํ์ฌ state๋ก, ์ฒ์์ ์ ๊ณตํ ์ด๊ธฐ state๋ก ์ค์ ๋ฉ๋๋ค.
- ์ํธ์์ฉ์ ๋ฐ์ํ์ฌ ๋ค๋ฅธ ๊ฐ์ผ๋ก ๋ณ๊ฒฝํ ์ ์๋
set
ํจ์์ ๋๋ค.
ํ๋ฉด์ ๋ด์ฉ์ ์
๋ฐ์ดํธํ๋ ค๋ฉด ๋ค์ state๋ก set
ํจ์๋ฅผ ํธ์ถํฉ๋๋ค.
function handleClick() {
setName('Robin');
}
React๋ ๋ค์ state๋ฅผ ์ ์ฅํ๊ณ ์๋ก์ด ๊ฐ์ผ๋ก ์ปดํฌ๋ํธ๋ฅผ ๋ค์ ๋ ๋๋งํ ํ UI๋ฅผ ์ ๋ฐ์ดํธํฉ๋๋ค.
set
ํจ์๋ฅผ ํธ์ถํด๋ ์ด๋ฏธ ์คํ ์ค์ธ ์ฝ๋์ ํ์ฌ state๋ ๋ณ๊ฒฝ๋์ง ์์ต๋๋ค.
function handleClick() {
setName('Robin');
console.log(name); // ์์ง "Taylor"์
๋๋ค!
}
set
ํจ์๋ ๋ค์ ๋ ๋๋ง์์ ๋ฐํํ useState
์๋ง ์ํฅ์ ์ค๋๋ค.
์์์์ count
state ๋ณ์๋ ์ซ์๋ฅผ ๋ฐ์ต๋๋ค. ๋ฒํผ์ ํด๋ฆญํ๋ฉด ์ซ์๊ฐ ์ฆ๊ฐํฉ๋๋ค.
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
You pressed me {count} times
</button>
);
}
์์์์ text
state ๋ณ์๋ ๋ฌธ์์ด์ ๋ฐ์ต๋๋ค. input์ ํ์ดํํ๋ฉด handleChange
๋ input DOM ์์์์ ์ต์ input ๊ฐ์ ์ฝ๊ณ setText
๋ฅผ ํธ์ถํ์ฌ state๋ฅผ ์
๋ฐ์ดํธํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์๋์ ํ์ฌ text
๋ฅผ ํ์ํ ์ ์์ต๋๋ค.
import { useState } from 'react';
export default function MyInput() {
const [text, setText] = useState('hello');
function handleChange(e) {
setText(e.target.value);
}
return (
<>
<input value={text} onChange={handleChange} />
<p>You typed: {text}</p>
<button onClick={() => setText('hello')}>
Reset
</button>
</>
);
}
์์์์ liked
state ๋ณ์๋ ๋ถ๋ฆฌ์ธ์ ๋ฐ์ต๋๋ค. input์ ํด๋ฆญํ๋ฉด setLiked
๋ ์ฒดํฌ๋ฐ์ค๊ฐ ์ ํ๋์ด ์๋์ง ์ฌ๋ถ์ ๋ฐ๋ผ liked
state ๋ณ์๋ฅผ ์
๋ฐ์ดํธํฉ๋๋ค. liked
๋ณ์๋ ์ฒดํฌ๋ฐ์ค ์๋์ ํ
์คํธ๋ฅผ ๋ ๋๋งํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
import { useState } from 'react';
export default function MyCheckbox() {
const [liked, setLiked] = useState(true);
function handleChange(e) {
setLiked(e.target.checked);
}
return (
<>
<label>
<input
type="checkbox"
checked={liked}
onChange={handleChange}
/>
I liked this
</label>
<p>You {liked ? 'liked' : 'did not like'} this.</p>
</>
);
}
๋์ผํ ์ปดํฌ๋ํธ์ ๋ ๊ฐ ์ด์์ state ๋ณ์๋ฅผ ์ ์ธํ ์ ์์ต๋๋ค. ๊ฐ state ๋ณ์๋ ์์ ํ ๋ ๋ฆฝ์ ์ ๋๋ค.
import { useState } from 'react';
export default function Form() {
const [name, setName] = useState('Taylor');
const [age, setAge] = useState(42);
return (
<>
<input
value={name}
onChange={e => setName(e.target.value)}
/>
<button onClick={() => setAge(age + 1)}>
Increment age
</button>
<p>Hello, {name}. You are {age}.</p>
</>
);
}
button { display: block; margin-top: 10px; }
์ด์ state๋ฅผ ๊ธฐ๋ฐ์ผ๋ก state ์ ๋ฐ์ดํธํ๊ธฐ {/updating-state-based-on-the-previous-state/}
age
๊ฐ 42
๋ผ๊ณ ๊ฐ์ ํฉ๋๋ค. ์ด ํธ๋ค๋ฌ๋ setAge(age + 1)
๋ฅผ ์ธ ๋ฒ ํธ์ถํฉ๋๋ค.
function handleClick() {
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
}
ํ์ง๋ง ํด๋ฆญํด๋ณด๋ฉด age
๋ 45
๊ฐ ์๋๋ผ 43
์ด ๋ฉ๋๋ค! ์ด๋ set
ํจ์๋ฅผ ํธ์ถํด๋ ์ด๋ฏธ ์คํ ์ค์ธ ์ฝ๋์์ age
state ๋ณ์๊ฐ ์
๋ฐ์ดํธ๋์ง ์๊ธฐ ๋๋ฌธ์
๋๋ค. ๋ฐ๋ผ์ ๊ฐ setAge(age + 1)
ํธ์ถ์ setAge(43)
์ด ๋ฉ๋๋ค.
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด ๋ค์ state ๋์ setAge
์ ์
๋ฐ์ดํฐ ํจ์๋ฅผ ์ ๋ฌํ ์ ์์ต๋๋ค.
function handleClick() {
setAge(a => a + 1); // setAge(42 => 43)
setAge(a => a + 1); // setAge(43 => 44)
setAge(a => a + 1); // setAge(44 => 45)
}
์ฌ๊ธฐ์ a => a + 1
์ ์
๋ฐ์ดํฐ ํจ์์
๋๋ค. ์ด ํจ์๋ ๋๊ธฐ ์ค์ธ state๋ฅผ ๊ฐ์ ธ์์ ๋ค์ state๋ฅผ ๊ณ์ฐํฉ๋๋ค.
React๋ ์ ๋ฐ์ดํฐ ํจ์๋ฅผ ํ์ ๋ฃ์ต๋๋ค. ๊ทธ๋ฌ๋ฉด ๋ค์ ๋ ๋๋ง ์ค์ ๋์ผํ ์์๋ก ํธ์ถํฉ๋๋ค.
a => a + 1
์ ๋๊ธฐ ์ค์ธ state๋ก42
๋ฅผ ๋ฐ๊ณ ๋ค์ state๋ก43
์ ๋ฐํํฉ๋๋ค.a => a + 1
์ ๋๊ธฐ ์ค์ธ state๋ก43
์ ๋ฐ๊ณ ๋ค์ state๋ก44
๋ฅผ ๋ฐํํฉ๋๋ค.a => a + 1
์ ๋๊ธฐ ์ค์ธ state๋ก44
๋ฅผ ๋ฐ๊ณ ๋ค์ state๋ก45
๋ฅผ ๋ฐํํฉ๋๋ค.
๋๊ธฐ ์ค์ธ ๋ค๋ฅธ ์
๋ฐ์ดํธ๊ฐ ์์ผ๋ฏ๋ก, React๋ ๊ฒฐ๊ตญ 45
๋ฅผ ํ์ฌ state๋ก ์ ์ฅํฉ๋๋ค.
๊ท์น์ ๋๊ธฐ ์ค์ธ state ์ธ์์ ์ด๋ฆ์ age
์ a
์ ๊ฐ์ด state ๋ณ์ ์ด๋ฆ์ ์ฒซ ๊ธ์๋ก ์ง์ ํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์
๋๋ค. ๊ทธ๋ฌ๋ prevAge
๋๋ ๋ ๋ช
ํํ๋ค๊ณ ์๊ฐํ๋ ๋ค๋ฅธ ์ด๋ฆ์ผ๋ก ์ง์ ํด๋ ๋ฉ๋๋ค.
React๋ ๊ฐ๋ฐ ํ๊ฒฝ์์ ์์ํ์ง ํ์ธํ๊ธฐ ์ํด ์ ๋ฐ์ดํฐ๋ฅผ ๋ ๋ฒ ํธ์ถํ ์ ์์ต๋๋ค.
ํญ์ ์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ์ข์๊ฐ์? {/is-using-an-updater-always-preferred/}
์ค์ ํ๋ ค๋ state๊ฐ ์ด์ state์์ ๊ณ์ฐ๋๋ ๊ฒฝ์ฐ ํญ์ setAge(a => a + 1)
์ฒ๋ผ ์
๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๋๊ฑธ ์ถ์ฒํ๋ค๋ ๋ง์ ๋ค์ด๋ณด์์ ๊ฒ์
๋๋ค. ๋์ ๊ฑด ์์ง๋ง ํญ์ ๊ทธ๋์ผ๋ง ํ๋ ๊ฒ์ ์๋๋๋ค.
๋๋ถ๋ถ์ ๊ฒฝ์ฐ, ์ด ๋ ๊ฐ์ง ์ ๊ทผ ๋ฐฉ์ ์ฌ์ด์๋ ์ฐจ์ด๊ฐ ์์ต๋๋ค. React๋ ํด๋ฆญ๊ณผ ๊ฐ์ ์๋์ ์ธ ์ฌ์ฉ์ ์ก์
์ ๋ํด ํญ์ ๋ค์ ํด๋ฆญ ์ ์ age
state ๋ณ์๊ฐ ์
๋ฐ์ดํธ ๋๋๋ก ํฉ๋๋ค. ์ฆ, ํด๋ฆญ ํธ๋ค๋ฌ๊ฐ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ์์ํ ๋ "์ค๋๋" age
๋ฅผ ๋ณผ ์ํ์ ์์ต๋๋ค.
๋ค๋ง ๋์ผํ ์ด๋ฒคํธ ๋ด์์ ์ฌ๋ฌ ์ ๋ฐ์ดํธ๋ฅผ ์ํํ๋ ๊ฒฝ์ฐ์๋ ์ ๋ฐ์ดํฐ๊ฐ ๋์์ด ๋ ์ ์์ต๋๋ค. state ๋ณ์ ์์ฒด์ ์ ๊ทผํ๋ ๊ฒ์ด ์ด๋ ค์ด ๊ฒฝ์ฐ์๋ ์ ์ฉํฉ๋๋ค. (๋ฆฌ๋ ๋๋ง์ ์ต์ ํํ ๋ ์ด ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค).
์น์ ํ ๋ฌธ๋ฒ๋ณด๋ค ์ผ๊ด์ฑ์ ๋ ์ ํธํ๋ค๋ฉด ์ค์ ํ๋ ค๋ state๊ฐ ์ด์ state์์ ๊ณ์ฐ๋๋ ๊ฒฝ์ฐ ํญ์ ์ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ๋ ๊ฒ์ด ํฉ๋ฆฌ์ ์ผ ๊ฒ์ ๋๋ค. ๋ง์ฝ ์ด๋ค state๊ฐ ๋ค๋ฅธ state ๋ณ์์ ์ด์ state๋ก๋ถํฐ ๊ณ์ฐ๋๋ ๊ฒฝ์ฐ๋ผ๋ฉด, ์ด๋ฅผ ํ๋์ ๊ฐ์ฒด๋ก ๊ฒฐํฉํ๊ณ reducer๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
์ด ์์๋ ์ ๋ฐ์ดํฐ ํจ์๋ฅผ ์ ๋ฌํ๋ฏ๋ก "+3" ๋ฒํผ์ด ์๋ํฉ๋๋ค.
import { useState } from 'react';
export default function Counter() {
const [age, setAge] = useState(42);
function increment() {
setAge(a => a + 1);
}
return (
<>
<h1>Your age: {age}</h1>
<button onClick={() => {
increment();
increment();
increment();
}}>+3</button>
<button onClick={() => {
increment();
}}>+1</button>
</>
);
}
button { display: block; margin: 10px; font-size: 20px; }
h1 { display: block; margin: 10px; }
์ด ์์๋ ์ ๋ฐ์ดํฐ ํจ์๋ฅผ ์ ๋ฌํ์ง ์์ผ๋ฏ๋ก "+3" ๋ฒํผ์ด ์๋ํ ๋๋ก ์๋ํ์ง ์์ต๋๋ค.
import { useState } from 'react';
export default function Counter() {
const [age, setAge] = useState(42);
function increment() {
setAge(age + 1);
}
return (
<>
<h1>Your age: {age}</h1>
<button onClick={() => {
increment();
increment();
increment();
}}>+3</button>
<button onClick={() => {
increment();
}}>+1</button>
</>
);
}
button { display: block; margin: 10px; font-size: 20px; }
h1 { display: block; margin: 10px; }
state์๋ ๊ฐ์ฒด์ ๋ฐฐ์ด๋ ๋ฃ์ ์ ์์ต๋๋ค. React์์ state๋ ์ฝ๊ธฐ ์ ์ฉ์ผ๋ก ๊ฐ์ฃผ๋๋ฏ๋ก ๊ธฐ์กด ๊ฐ์ฒด๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ , ๊ต์ฒด ํด์ผ ํฉ๋๋ค. ์๋ฅผ ๋ค์ด, state์ form
๊ฐ์ฒด๊ฐ ์๋ ๊ฒฝ์ฐ ๋ณ๊ฒฝํ์ง ๋ง์ธ์.
// ๐ฉ state ์์ ์๋ ๊ฐ์ฒด๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๋ณ๊ฒฝํ์ง ๋ง์ธ์.
form.firstName = 'Taylor';
๋์ ์๋ก์ด ๊ฐ์ฒด๋ฅผ ์์ฑํ์ฌ ์ ์ฒด ๊ฐ์ฒด๋ฅผ ๊ต์ฒดํ์ธ์.
// โ
์๋ก์ด ๊ฐ์ฒด๋ก state๋ฅผ ๊ต์ฒดํฉ๋๋ค.
setForm({
...form,
firstName: 'Taylor'
});
์์ธํ ๋ด์ฉ์ ๊ฐ์ฒด state ์ ๋ฐ์ดํธํ๊ธฐ ๋ฐ ๋ฐฐ์ด state ์ ๋ฐ์ดํธํ๊ธฐ์์ ํ์ธํ์ธ์.
์ด ์์์์ form
state ๋ณ์๋ ๊ฐ์ฒด๋ฅผ ๋ฐ์ต๋๋ค. ๊ฐ input์๋ ์ ์ฒด form
์ ๋ค์ state๋ก setForm
์ ํธ์ถํ๋ change ํธ๋ค๋ฌ๊ฐ ์์ต๋๋ค. ์ ๊ฐ ๊ตฌ๋ฌธ์ธ { ...form }
์ state ๊ฐ์ฒด๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ ๊ต์ฒดํฉ๋๋ค.
import { useState } from 'react';
export default function Form() {
const [form, setForm] = useState({
firstName: 'Barbara',
lastName: 'Hepworth',
email: '[email protected]',
});
return (
<>
<label>
First name:
<input
value={form.firstName}
onChange={e => {
setForm({
...form,
firstName: e.target.value
});
}}
/>
</label>
<label>
Last name:
<input
value={form.lastName}
onChange={e => {
setForm({
...form,
lastName: e.target.value
});
}}
/>
</label>
<label>
Email:
<input
value={form.email}
onChange={e => {
setForm({
...form,
email: e.target.value
});
}}
/>
</label>
<p>
{form.firstName}{' '}
{form.lastName}{' '}
({form.email})
</p>
</>
);
}
label { display: block; }
input { margin-left: 5px; }
์ด ์์์์๋ state๊ฐ ๋ ์ค์ฒฉ๋์ด ์์ต๋๋ค. ์ค์ฒฉ๋ state๋ฅผ ์ ๋ฐ์ดํธํ ๋๋ ์ ๋ฐ์ดํธํ๋ ค๋ ๊ฐ์ฒด์ ๋ณต์ฌ๋ณธ์ ๋ง๋ค์ด์ผ ํ๋ฉฐ, ์์ชฝ์ผ๋ก ์ฌ๋ผ๊ฐ ๋๋ง๋ค ํด๋น ๊ฐ์ฒด๋ฅผ "ํฌํจํ๋" ๋ชจ๋ ๊ฐ์ฒด์ ๋ํ ๋ณต์ฌ๋ณธ์ ๋ง๋ค์ด์ผ ํฉ๋๋ค. ์์ธํ ์์๋ณด๋ ค๋ฉด ์ค์ฒฉ๋ ๊ฐ์ฒด ์ ๋ฐ์ดํธํ๊ธฐ๋ฅผ ์ฝ์ด๋ณด์ธ์.
import { useState } from 'react';
export default function Form() {
const [person, setPerson] = useState({
name: 'Niki de Saint Phalle',
artwork: {
title: 'Blue Nana',
city: 'Hamburg',
image: 'https://i.imgur.com/Sd1AgUOm.jpg',
}
});
function handleNameChange(e) {
setPerson({
...person,
name: e.target.value
});
}
function handleTitleChange(e) {
setPerson({
...person,
artwork: {
...person.artwork,
title: e.target.value
}
});
}
function handleCityChange(e) {
setPerson({
...person,
artwork: {
...person.artwork,
city: e.target.value
}
});
}
function handleImageChange(e) {
setPerson({
...person,
artwork: {
...person.artwork,
image: e.target.value
}
});
}
return (
<>
<label>
Name:
<input
value={person.name}
onChange={handleNameChange}
/>
</label>
<label>
Title:
<input
value={person.artwork.title}
onChange={handleTitleChange}
/>
</label>
<label>
City:
<input
value={person.artwork.city}
onChange={handleCityChange}
/>
</label>
<label>
Image:
<input
value={person.artwork.image}
onChange={handleImageChange}
/>
</label>
<p>
<i>{person.artwork.title}</i>
{' by '}
{person.name}
<br />
(located in {person.artwork.city})
</p>
<img
src={person.artwork.image}
alt={person.artwork.title}
/>
</>
);
}
label { display: block; }
input { margin-left: 5px; margin-bottom: 5px; }
img { width: 200px; height: 200px; }
์ด ์์์์ todos
state ๋ณ์๋ ๋ฐฐ์ด์ ๋ฐ์ต๋๋ค. ๊ฐ ๋ฒํผ ํธ๋ค๋ฌ๋ ํด๋น ๋ฐฐ์ด์ ๋ค์ ๋ฒ์ ์ผ๋ก setTodos
๋ฅผ ํธ์ถํฉ๋๋ค. [...todos]
์ ๊ฐ ๊ตฌ๋ฌธ, todos.map()
๋ฐ todos.filter()
๋ state ๋ฐฐ์ด์ด ๋ณ๊ฒฝํ์ง ์๊ณ ๊ต์ฒดํฉ๋๋ค.
import { useState } from 'react';
import AddTodo from './AddTodo.js';
import TaskList from './TaskList.js';
let nextId = 3;
const initialTodos = [
{ id: 0, title: 'Buy milk', done: true },
{ id: 1, title: 'Eat tacos', done: false },
{ id: 2, title: 'Brew tea', done: false },
];
export default function TaskApp() {
const [todos, setTodos] = useState(initialTodos);
function handleAddTodo(title) {
setTodos([
...todos,
{
id: nextId++,
title: title,
done: false
}
]);
}
function handleChangeTodo(nextTodo) {
setTodos(todos.map(t => {
if (t.id === nextTodo.id) {
return nextTodo;
} else {
return t;
}
}));
}
function handleDeleteTodo(todoId) {
setTodos(
todos.filter(t => t.id !== todoId)
);
}
return (
<>
<AddTodo
onAddTodo={handleAddTodo}
/>
<TaskList
todos={todos}
onChangeTodo={handleChangeTodo}
onDeleteTodo={handleDeleteTodo}
/>
</>
);
}
import { useState } from 'react';
export default function AddTodo({ onAddTodo }) {
const [title, setTitle] = useState('');
return (
<>
<input
placeholder="Add todo"
value={title}
onChange={e => setTitle(e.target.value)}
/>
<button onClick={() => {
setTitle('');
onAddTodo(title);
}}>Add</button>
</>
)
}
import { useState } from 'react';
export default function TaskList({
todos,
onChangeTodo,
onDeleteTodo
}) {
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
<Task
todo={todo}
onChange={onChangeTodo}
onDelete={onDeleteTodo}
/>
</li>
))}
</ul>
);
}
function Task({ todo, onChange, onDelete }) {
const [isEditing, setIsEditing] = useState(false);
let todoContent;
if (isEditing) {
todoContent = (
<>
<input
value={todo.title}
onChange={e => {
onChange({
...todo,
title: e.target.value
});
}} />
<button onClick={() => setIsEditing(false)}>
Save
</button>
</>
);
} else {
todoContent = (
<>
{todo.title}
<button onClick={() => setIsEditing(true)}>
Edit
</button>
</>
);
}
return (
<label>
<input
type="checkbox"
checked={todo.done}
onChange={e => {
onChange({
...todo,
done: e.target.checked
});
}}
/>
{todoContent}
<button onClick={() => onDelete(todo.id)}>
Delete
</button>
</label>
);
}
button { margin: 5px; }
li { list-style-type: none; }
ul, li { margin: 0; padding: 0; }
๋ณ๊ฒฝ ์์ด ๋ฐฐ์ด๊ณผ ๊ฐ์ฒด๋ฅผ ์ ๋ฐ์ดํธํ๋ ๊ฒ์ด ๊ท์ฐฎ๊ฒ ๋๊ปด์ง๋ค๋ฉด Immer์ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ๋ณต์ ์ธ ์ฝ๋๋ฅผ ์ค์ผ ์ ์์ต๋๋ค. Immer๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐ์ฒด๋ฅผ ๋ณ๊ฒฝํ๋ ๊ฒ์ฒ๋ผ ์ฝ๋๋ฅผ ๊ฐ๊ฒฐํ๊ฒ ์์ฑํ๋๋ผ๋ ๋ด๋ถ์ ์ผ๋ก๋ ๋ถ๋ณ์ฑ์ ์ ์งํ ์ ๋ฐ์ดํธ๋ฅผ ์ํํฉ๋๋ค.
import { useState } from 'react';
import { useImmer } from 'use-immer';
let nextId = 3;
const initialList = [
{ id: 0, title: 'Big Bellies', seen: false },
{ id: 1, title: 'Lunar Landscape', seen: false },
{ id: 2, title: 'Terracotta Army', seen: true },
];
export default function BucketList() {
const [list, updateList] = useImmer(initialList);
function handleToggle(artworkId, nextSeen) {
updateList(draft => {
const artwork = draft.find(a =>
a.id === artworkId
);
artwork.seen = nextSeen;
});
}
return (
<>
<h1>Art Bucket List</h1>
<h2>My list of art to see:</h2>
<ItemList
artworks={list}
onToggle={handleToggle} />
</>
);
}
function ItemList({ artworks, onToggle }) {
return (
<ul>
{artworks.map(artwork => (
<li key={artwork.id}>
<label>
<input
type="checkbox"
checked={artwork.seen}
onChange={e => {
onToggle(
artwork.id,
e.target.checked
);
}}
/>
{artwork.title}
</label>
</li>
))}
</ul>
);
}
{
"dependencies": {
"immer": "1.7.3",
"react": "latest",
"react-dom": "latest",
"react-scripts": "latest",
"use-immer": "0.5.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
React๋ ์ด๊ธฐ state๋ฅผ ํ ๋ฒ ์ ์ฅํ๊ณ ๋ค์ ๋ ๋๋ง๋ถํฐ๋ ์ด๋ฅผ ๋ฌด์ํฉ๋๋ค.
function TodoList() {
const [todos, setTodos] = useState(createInitialTodos());
// ...
createInitialTodos()
์ ๊ฒฐ๊ณผ๋ ์ด๊ธฐ ๋ ๋๋ง์๋ง ์ฌ์ฉ๋์ง๋ง, ์ฌ์ ํ ๋ชจ๋ ๋ ๋๋ง์์ ์ด ํจ์๋ฅผ ํธ์ถํฉ๋๋ค. ์ด๋ ํฐ ๋ฐฐ์ด์ ์์ฑํ๊ฑฐ๋ ๊ฐ๋น์ผ ๊ณ์ฐ์ ์ํํ๋ ๊ฒฝ์ฐ ๋ญ๋น์ผ ์ ์์ต๋๋ค.
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด, useState
์ ์ด๊ธฐํ ํจ์๋ก ์ ๋ฌํ์ธ์.
function TodoList() {
const [todos, setTodos] = useState(createInitialTodos);
// ...
ํจ์๋ฅผ ํธ์ถํ ๊ฒฐ๊ณผ์ธ createInitialTodos()
๊ฐ ์๋๋ผ ํจ์ ์์ฒด์ธ createInitialTodos
๋ฅผ ์ ๋ฌํ๊ณ ์๋ค๋ ๊ฒ์ ์ฃผ๋ชฉํ์ธ์. ํจ์๋ฅผ useState
์ ์ ๋ฌํ๋ฉด React๋ ์ด๊ธฐํ ์ค์๋ง ํจ์๋ฅผ ํธ์ถํฉ๋๋ค.
๊ฐ๋ฐ ํ๊ฒฝ์์๋ React๊ฐ ์ด๊ธฐํ ํจ์๊ฐ ์์ํ์ง ํ์ธํ๊ธฐ ์ํด ์ด๊ธฐํ ํจ์๋ฅผ ๋ ๋ฒ ํธ์ถํ ์ ์์ต๋๋ค.
์ด ์์์์๋ ์ด๊ธฐํ ํจ์๋ฅผ ์ ๋ฌํ๋ฏ๋ก, createInitialTodos
ํจ์๋ ์ด๊ธฐํ ์ค์๋ง ์คํ๋ฉ๋๋ค. input์ ํ์ดํํ ๋ ๊ฐ์ด ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋งํ ๋์๋ ์คํ๋์ง ์์ต๋๋ค.
import { useState } from 'react';
function createInitialTodos() {
const initialTodos = [];
for (let i = 0; i < 50; i++) {
initialTodos.push({
id: i,
text: 'Item ' + (i + 1)
});
}
return initialTodos;
}
export default function TodoList() {
const [todos, setTodos] = useState(createInitialTodos);
const [text, setText] = useState('');
return (
<>
<input
value={text}
onChange={e => setText(e.target.value)}
/>
<button onClick={() => {
setText('');
setTodos([{
id: todos.length,
text: text
}, ...todos]);
}}>Add</button>
<ul>
{todos.map(item => (
<li key={item.id}>
{item.text}
</li>
))}
</ul>
</>
);
}
์ด ์์์์๋ ์ด๊ธฐํ ํจ์๋ฅผ ์ ๋ฌํ์ง ์์ผ๋ฏ๋ก, input์ ํ์ดํํ ๋ ๊ฐ์ด ๋ชจ๋ ๋ ๋๋ง์์ createInitialTodos
ํจ์๊ฐ ์คํ๋ฉ๋๋ค. ๋์์ ๋์ ๋๋ ์ฐจ์ด๋ ์์ง๋ง ์ด ์ฝ๋๋ ํจ์จ์ฑ์ด ๋จ์ด์ง๋๋ค.
import { useState } from 'react';
function createInitialTodos() {
const initialTodos = [];
for (let i = 0; i < 50; i++) {
initialTodos.push({
id: i,
text: 'Item ' + (i + 1)
});
}
return initialTodos;
}
export default function TodoList() {
const [todos, setTodos] = useState(createInitialTodos());
const [text, setText] = useState('');
return (
<>
<input
value={text}
onChange={e => setText(e.target.value)}
/>
<button onClick={() => {
setText('');
setTodos([{
id: todos.length,
text: text
}, ...todos]);
}}>Add</button>
<ul>
{todos.map(item => (
<li key={item.id}>
{item.text}
</li>
))}
</ul>
</>
);
}
๋ชฉ๋ก์ ๋ ๋๋งํ ๋ key
์์ฑ์ ์์ฃผ ์ ํ๊ฒ ๋ฉ๋๋ค. ํ์ง๋ง key
์์ฑ์ ๋ค๋ฅธ ์ฉ๋๋ก๋ ์ฌ์ฉ๋ฉ๋๋ค.
์ปดํฌ๋ํธ์ ๋ค๋ฅธ key
๋ฅผ ์ ๋ฌํ์ฌ ์ปดํฌ๋ํธ์ state๋ฅผ ์ด๊ธฐํํ ์ ์์ต๋๋ค. ์ด ์์์์๋ Reset ๋ฒํผ์ด version
state ๋ณ์๋ฅผ ๋ณ๊ฒฝํ๊ณ , ์ด๋ฅผ Form
์ key
๋ก ์ ๋ฌํฉ๋๋ค. key
๊ฐ ๋ณ๊ฒฝ๋๋ฉด React๋ Form
์ปดํฌ๋ํธ(๋ฐ ๊ทธ ๋ชจ๋ ์์)๋ฅผ ์ฒ์๋ถํฐ ๋ค์ ์์ฑํ๋ฏ๋ก state๊ฐ ์ด๊ธฐํ๋ฉ๋๋ค.
์์ธํ ์์๋ณด๋ ค๋ฉด State๋ฅผ ๋ณด์กดํ๊ณ ์ด๊ธฐํํ๊ธฐ๋ฅผ ์ฝ์ด๋ณด์ธ์.
import { useState } from 'react';
export default function App() {
const [version, setVersion] = useState(0);
function handleReset() {
setVersion(version + 1);
}
return (
<>
<button onClick={handleReset}>Reset</button>
<Form key={version} />
</>
);
}
function Form() {
const [name, setName] = useState('Taylor');
return (
<>
<input
value={name}
onChange={e => setName(e.target.value)}
/>
<p>Hello, {name}.</p>
</>
);
}
button { display: block; margin-bottom: 20px; }
๋ณดํต์ ์ด๋ฒคํธ ํธ๋ค๋ฌ์์ state๋ฅผ ์ ๋ฐ์ดํธํฉ๋๋ค. ํ์ง๋ง ๋๋ฌผ๊ฒ ๋ ๋๋ง์ ๋ํ ์๋ต์ผ๋ก state๋ฅผ ์กฐ์ ํด์ผ ํ๋ ๊ฒฝ์ฐ๋ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, props๊ฐ ๋ณ๊ฒฝ๋ ๋ state ๋ณ์๋ฅผ ๋ณ๊ฒฝํ๊ณ ์ถ์ ์ ์์ต๋๋ค.
๋๋ถ๋ถ์ ๊ฒฝ์ฐ ์ด ๊ธฐ๋ฅ์ ํ์ํ์ง ์์ต๋๋ค.
- ํ์ํ ๊ฐ์ ํ์ฌ props๋ ๋ค๋ฅธ state์์ ๋ชจ๋ ๊ณ์ฐํ ์ ์๋ ๊ฒฝ์ฐ, ์ค๋ณต๋๋ state๋ฅผ ๋ชจ๋ ์ ๊ฑฐํ์ธ์. ๋๋ฌด ์์ฃผ ์ฌ๊ณ์ฐํ๋ ๊ฒ์ด ๊ฑฑ์ ๋๋ค๋ฉด,
useMemo
Hook์ ์ฌ์ฉํ๋ฉด ๋์์ด ๋ ์ ์์ต๋๋ค. - ์ ์ฒด ์ปดํฌ๋ํธ ํธ๋ฆฌ์ state๋ฅผ ์ด๊ธฐํํ๋ ค๋ฉด ์ปดํฌ๋ํธ์ ๋ค๋ฅธ
key
๋ฅผ ์ ๋ฌํ์ธ์. - ๊ฐ๋ฅํ๋ค๋ฉด ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ๋ชจ๋ ๊ด๋ จ state๋ฅผ ์ ๋ฐ์ดํธํ์ธ์.
์ด ์ค ์ด๋ ๊ฒ์๋ ํด๋นํ์ง ์๋ ํฌ๊ทํ ๊ฒฝ์ฐ์๋, ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง๋๋ ๋์ set
ํจ์๋ฅผ ํธ์ถํ์ฌ ์ง๊ธ๊น์ง ๋ ๋๋ง๋ ๊ฐ์ ๋ฐํ์ผ๋ก state๋ฅผ ์
๋ฐ์ดํธํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ ํจํด์ด ์์ต๋๋ค.
๋ค์์ ๊ทธ ์์์
๋๋ค. CountLabel
์ปดํฌ๋ํธ๋ ์ ๋ฌ๋ count
props๋ฅผ ํ์ํฉ๋๋ค.
export default function CountLabel({ count }) {
return <h1>{count}</h1>
}
์นด์ดํฐ๊ฐ ๋ง์ง๋ง ๋ณ๊ฒฝ ์ดํ ์ฆ๊ฐ ๋๋ ๊ฐ์ํ๋์ง๋ฅผ ํ์ํ๊ณ ์ถ๋ค๊ณ ๊ฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค. count
prop๋ ์ด๋ฅผ ์๋ ค์ฃผ์ง ์์ผ๋ฏ๋ก ์ด์ ๊ฐ์ ์ถ์ ํด์ผ ํฉ๋๋ค. ์ด๋ฅผ ์ถ์ ํ๊ธฐ ์ํด prevCount
state ๋ณ์๋ฅผ ์ถ๊ฐํฉ๋๋ค. trend
๋ผ๋ ๋ ๋ค๋ฅธ state ๋ณ์๋ฅผ ์ถ๊ฐํ์ฌ count์ ์ฆ๊ฐ ๋๋ ๊ฐ์ ์ฌ๋ถ๋ฅผ ์ถ์ ํฉ๋๋ค. prevCount
์ count
๋ฅผ ๋น๊ตํด์, ๊ฐ์ง ์์ ๊ฒฝ์ฐ prevCount
์ trend
๋ฅผ ๋ชจ๋ ์
๋ฐ์ดํธํฉ๋๋ค. ์ด์ ํ์ฌ count props์ ๋ง์ง๋ง ๋ ๋๋ง ์ดํ count๊ฐ ์ด๋ป๊ฒ ๋ณ๊ฒฝ๋์๋์ง ๋ชจ๋ ํ์ํ ์ ์์ต๋๋ค.
import { useState } from 'react';
import CountLabel from './CountLabel.js';
export default function App() {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
<button onClick={() => setCount(count - 1)}>
Decrement
</button>
<CountLabel count={count} />
</>
);
}
import { useState } from 'react';
export default function CountLabel({ count }) {
const [prevCount, setPrevCount] = useState(count);
const [trend, setTrend] = useState(null);
if (prevCount !== count) {
setPrevCount(count);
setTrend(count > prevCount ? 'increasing' : 'decreasing');
}
return (
<>
<h1>{count}</h1>
{trend && <p>The count is {trend}</p>}
</>
);
}
button { margin-bottom: 10px; }
๋ ๋๋งํ๋ ๋์ set
ํจ์๋ฅผ ํธ์ถํ๋ ๊ฒฝ์ฐ, ๊ทธ set
ํจ์๋ prevCount !== count
์ ๊ฐ์ ์กฐ๊ฑด ์์ ์์ด์ผ ํ๋ฉฐ, ์กฐ๊ฑด ๋ด๋ถ์ setPrevCount(count)
์ ๊ฐ์ ํธ์ถ์ด ์์ด์ผ ํ๋ค๋ ์ ์ ์ ์ํ์ธ์. ๊ทธ๋ ์ง ์์ผ๋ฉด ๋ฆฌ๋ ๋๋ง์ ๋ฐ๋ณตํ๋ค๊ฐ ๊ฒฐ๊ตญ ๊นจ์ง ๊ฒ์
๋๋ค. ๋ํ ์ด ๋ฐฉ์์ ์ค์ง ํ์ฌ ๋ ๋๋ง ์ค์ธ ์ปดํฌ๋ํธ์ state๋ง์ ์
๋ฐ์ดํธํ ์ ์์ต๋๋ค. ๋ ๋๋ง ์ค์ ๋ค๋ฅธ ์ปดํฌ๋ํธ์ set
ํจ์๋ฅผ ํธ์ถํ๋ ๊ฒ์ ์๋ฌ์
๋๋ค. ๋ง์ง๋ง์ผ๋ก, ์ด ๊ฒฝ์ฐ์๋ set
ํจ์ ํธ์ถ์ ์ฌ์ ํ ๋ณ๊ฒฝ์ด ์๋ state ์
๋ฐ์ดํธ์ฌ์ผ๋ง ํฉ๋๋ค. ์์ ํจ์์ ๋ค๋ฅธ ๊ท์น์ ์ด๊ฒจ๋ ๋๋ค๋ ์๋ฏธ๊ฐ ์๋๋๋ค.
์ด ํจํด์ ์ดํดํ๊ธฐ ์ด๋ ค์ธ ์ ์์ผ๋ฉฐ ์ผ๋ฐ์ ์ผ๋ก ํผํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ข์ต๋๋ค. ํ์ง๋ง Effect์์ state๋ฅผ ์
๋ฐ์ดํธํ๋ ๊ฒ๋ณด๋ค๋ ๋ซ์ต๋๋ค. ๋ ๋๋ง ๋์ค set
ํจ์๋ฅผ ํธ์ถํ๋ฉด React๋ ์ปดํฌ๋ํธ๊ฐ return
๋ฌธ์ผ๋ก ์ข
๋ฃ๋ ์งํ, ์์์ ๋ ๋๋งํ๊ธฐ ์ ์ ํด๋น ์ปดํฌ๋ํธ๋ฅผ ๋ฆฌ๋ ๋๋ง ํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์์ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋ฒ ๋ ๋๋งํ ํ์๊ฐ ์์ต๋๋ค. ๋๋จธ์ง ์ปดํฌ๋ํธ ํจ์๋ ๊ณ์ ์คํ๋๊ณ ๊ฒฐ๊ณผ๋ ๋ฒ๋ ค์ง๋๋ค. ์กฐ๊ฑด์ด ๋ชจ๋ Hook ํธ์ถ๋ณด๋ค ์๋์ ์์ผ๋ฉด ์ด๋ฅธ(early) return;
์ ํตํด ๋ ๋๋ง์ ๋ ์ผ์ฐ ๋ค์ ์์ํ ์ ์์ต๋๋ค.
state๋ฅผ ์ ๋ฐ์ดํธํ์ง๋ง ๋ก๊ทธ์๋ ๊ณ์ ์ด์ ๊ฐ์ด ํ์๋ฉ๋๋ค {/ive-updated-the-state-but-logging-gives-me-the-old-value/}
set
ํจ์๋ฅผ ํธ์ถํด๋ ์คํ ์ค์ธ ์ฝ๋์ state๋ ๋ณ๊ฒฝ๋์ง ์์ต๋๋ค.
function handleClick() {
console.log(count); // 0
setCount(count + 1); // 1๋ก ๋ฆฌ๋ ๋๋ง ์์ฒญํฉ๋๋ค.
console.log(count); // ์์ง 0์
๋๋ค!
setTimeout(() => {
console.log(count); // ์ฌ๊ธฐ๋ 0์ด๊ณ ์!
}, 5000);
}
๊ทธ ์ด์ ๋ state๊ฐ ์ค๋
์ท์ฒ๋ผ ๋์ํ๊ธฐ ๋๋ฌธ์
๋๋ค. state๋ฅผ ์
๋ฐ์ดํธํ๋ฉด ์๋ก์ด state ๊ฐ์ผ๋ก ๋ค๋ฅธ ๋ ๋๋ง์ ์์ฒญํ์ง๋ง ์ด๋ฏธ ์คํ ์ค์ธ ์ด๋ฒคํธ ํธ๋ค๋ฌ์ count
๋ณ์์๋ ์ํฅ์ ๋ฏธ์น์ง ์์ต๋๋ค.
๋ค์ state๋ฅผ ์ฌ์ฉํด์ผ ํ๋ ๊ฒฝ์ฐ์๋, set
ํจ์์ ์ ๋ฌํ๊ธฐ ์ ์ ๋ณ์์ ์ ์ฅํ ์ ์์ต๋๋ค:
const nextCount = count + 1;
setCount(nextCount);
console.log(count); // 0
console.log(nextCount); // 1
state๋ฅผ ์ ๋ฐ์ดํธํด๋ ํ๋ฉด์ด ๋ฐ๋์ง ์์ต๋๋ค {/ive-updated-the-state-but-the-screen-doesnt-update/}
React๋ Object.is๋ก ๋น๊ตํ ๋ค ๋ค์ state๊ฐ ์ด์ state์ ๊ฐ์ผ๋ฉด ์ ๋ฐ์ดํธ๋ฅผ ๋ฌด์ํฉ๋๋ค. ์ด๋ ๋ณดํต ๊ฐ์ฒด๋ ๋ฐฐ์ด์ state๋ฅผ ์ง์ ๋ณ๊ฒฝํ ๋ ๋ฐ์ํฉ๋๋ค.
obj.x = 10; // ๐ฉ ์๋ชป๋ ๋ฐฉ๋ฒ: ๊ธฐ์กด ๊ฐ์ฒด๋ฅผ ๋ณ๊ฒฝ
setObj(obj); // ๐ฉ ์๋ฌด๊ฒ๋ ํ์ง ์์ต๋๋ค.
๊ธฐ์กด obj
๊ฐ์ฒด๋ฅผ ๋ณ๊ฒฝํ ํ ๋ค์ setObj
๋ก ์ ๋ฌํ๊ธฐ ๋๋ฌธ์ React๊ฐ ์
๋ฐ์ดํธ๋ฅผ ๋ฌด์ํ์ต๋๋ค. ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด ๊ฐ์ฒด๋ ๋ฐฐ์ด state๋ฅผ ๋ณ๊ฒฝํ๋ ๋์ ํญ์ ๊ต์ฒดํด์ผ ํฉ๋๋ค.
// โ
์ฌ๋ฐ๋ฅธ ๋ฐฉ๋ฒ: ์๋ก์ด ๊ฐ์ฒด ์์ฑ
setObj({
...obj,
x: 10
});
์๋ฌ๊ฐ ๋ฐ์ํ์ต๋๋ค: "๋ฆฌ๋ ๋๋ง ํ์๊ฐ ๋๋ฌด ๋ง์ต๋๋คโ {/im-getting-an-error-too-many-re-renders/}
๋ค์๊ณผ ๊ฐ์ ์๋ฌ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. ๋ฆฌ๋ ๋๋ง ํ์๊ฐ ๋๋ฌด ๋ง์ต๋๋ค. React๋ ๋ฌดํ ๋ฃจํ๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด ๋ ๋๋ง ํ์๋ฅผ ์ ํํฉ๋๋ค.
์ ํ์ ์ผ๋ก ์ด๋ ๋ ๋๋ง ์ค์ state๋ฅผ ๋ฌด์กฐ๊ฑด์ ์ผ๋ก ์ค์ ํ๊ณ ์์์ ์๋ฏธ ํ๊ธฐ ๋๋ฌธ์, ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง, state ์ค์ (๋ ๋๋ง ์ ๋ฐ), ๋ ๋๋ง, state ์ค์ (๋ ๋๋ง ์ ๋ฐ) ๋ฑ์ ๋ฃจํ์ ๋ค์ด๊ฐ๋ ๊ฒ์
๋๋ค. ์ด ๋ฌธ์ ๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ์ง์ ํ๋ ๊ณผ์ ์์ ์ค์๋ก ๋ฐ์ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค.
// ๐ฉ ์๋ชป๋ ๋ฐฉ๋ฒ: ๋ ๋๋ง ๋์ ํธ๋ค๋ฌ ์์ฒญ
return <button onClick={handleClick()}>Click me</button>
// โ
์ฌ๋ฐ๋ฅธ ๋ฐฉ๋ฒ: ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ก ์ ๋ฌ
return <button onClick={handleClick}>Click me</button>
// โ
์ฌ๋ฐ๋ฅธ ๋ฐฉ๋ฒ: ์ธ๋ผ์ธ ํจ์๋ก ์ ๋ฌ
return <button onClick={(e) => handleClick(e)}>Click me</button>
์ด ์๋ฌ์ ์์ธ์ ์ฐพ์ ์ ์๋ ๊ฒฝ์ฐ, ์ฝ์์์ ์๋ฌ ์์ ์๋ ํ์ดํ๋ฅผ ํด๋ฆญํ ๋ค JavaScript ์คํ์์ ์๋ฌ์ ์์ธ์ด ๋๋ ํน์ set
ํจ์ ํธ์ถ์ ์ฐพ์๋ณด์ธ์.
์ด๊ธฐํ ํจ์ ๋๋ ์ ๋ฐ์ดํฐ ํจ์๊ฐ ๋ ๋ฒ ์คํ๋ฉ๋๋ค {/my-initializer-or-updater-function-runs-twice/}
Strict Mode์์ React๋ ์ผ๋ถ ํจ์๋ฅผ ํ ๋ฒ์ด ์๋ ๋ ๋ฒ ํธ์ถํฉ๋๋ค.
function TodoList() {
// ํด๋น ํจ์ํ ์ปดํฌ๋ํธ๋ ๋ ๋๋ง ๋ ๋๋ง๋ค ๋ ๋ฒ ํธ์ถ๋ฉ๋๋ค.
const [todos, setTodos] = useState(() => {
// ํด๋น ์ด๊ธฐํ ํจ์๋ ์ด๊ธฐํ ๋์ ๋ ๋ฒ ํธ์ถ๋ฉ๋๋ค.
return createTodos();
});
function handleClick() {
setTodos(prevTodos => {
// ํด๋น ์
๋ฐ์ดํฐ ํจ์๋ ํด๋ฆญํ ๋๋ง๋ค ๋ ๋ฒ ํธ์ถ๋ฉ๋๋ค.
return [...prevTodos, createTodo()];
});
}
// ...
์ด๋ ์ ์์ ์ธ ํ์์ด๋ฉฐ, ์ด๋ก ์ธํด ์ฝ๋๊ฐ ์์๋์ง ์์์ผ ํฉ๋๋ค.
์ด ๊ฐ๋ฐ ํ๊ฒฝ ์ ์ฉ ๋์์ ์ปดํฌ๋ํธ๋ฅผ ์์ํ๊ฒ ์ ์งํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค. React๋ ํธ์ถ ์ค ํ๋์ ๊ฒฐ๊ณผ๋ฅผ ์ฌ์ฉํ๊ณ ๋ค๋ฅธ ํธ์ถ์ ๊ฒฐ๊ณผ๋ ๋ฌด์ํฉ๋๋ค. ์ปดํฌ๋ํธ, ์ด๊ธฐํ ํจ์, ์ ๋ฐ์ดํฐ ํจ์๊ฐ ์์ํ๋ค๋ฉด ์ด ๋์์ด ๋ก์ง์ ์ํฅ์ ๋ฏธ์น์ง ์์ต๋๋ค. ๋ฐ๋ฉด ์๋์น ์๊ฒ ์์ํ์ง ์์ ๊ฒฝ์ฐ์๋ ์ค์๋ฅผ ์์์ฐจ๋ฆฌ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
์๋ฅผ ๋ค์ด, ์์ํ์ง ์์ ์ ๋ฐ์ดํฐ ํจ์๋ state์ ๋ฐฐ์ด์ ๋ค์๊ณผ ๊ฐ์ด ๋ณ๊ฒฝํฉ๋๋ค.
setTodos(prevTodos => {
// ๐ฉ ์ค์: state ๋ณ๊ฒฝ
prevTodos.push(createTodo());
});
React๋ ์ ๋ฐ์ดํฐ ํจ์๋ฅผ ๋ ๋ฒ ํธ์ถํ๊ธฐ ๋๋ฌธ์ ํ ์ผ์ด ๋ ๋ฒ ์ถ๊ฐ๋์์์ ์ ์ ์์ผ๋ฏ๋ก, ์ค์๊ฐ ์์์ ํ์ ํ ์ ์์ต๋๋ค. ์ด ์์์์๋ ๋ฐฐ์ด์ ๋ณ๊ฒฝํ๋ ๋์ ๊ต์ฒดํ์ฌ ์ค์๋ฅผ ์์ ํ ์ ์์ต๋๋ค:
setTodos(prevTodos => {
// โ
์ฌ๋ฐ๋ฅธ ๋ฐฉ๋ฒ: ์๋ก์ด state๋ก ๊ต์ฒด
return [...prevTodos, createTodo()];
});
์ด์ ์ด ์ ๋ฐ์ดํฐ ํจ์๋ ์์ํ๋ฏ๋ก ํ ๋ฒ ๋ ํธ์ถํด๋ ๋์์ ์ฐจ์ด๊ฐ ์์ต๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ React๊ฐ ๋ ๋ฒ ํธ์ถํ๋ ๊ฒ์ด ์ค์๋ฅผ ์ฐพ๋ ๋ฐ ๋์์ด ๋๋ค๋ ๊ฒ์ ๋๋ค. ์ปดํฌ๋ํธ, ์ด๊ธฐํ ํจ์, ์ ๋ฐ์ดํฐ ํจ์๋ ์์ํด์ผ ํฉ๋๋ค. ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ์์ํ ํ์๊ฐ ์์ผ๋ฏ๋ก React๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ๋ ๋ฒ ํธ์ถํ์ง ์์ต๋๋ค.
์์ธํ ์์๋ณด๋ ค๋ฉด ์ปดํฌ๋ํธ ์์ํ๊ฒ ์ ์งํ๊ธฐ๋ฅผ ์ฝ์ด๋ณด์ธ์.
state์ ๊ฐ์ผ๋ก ํจ์๋ฅผ ์ค์ ํ๋ ค๊ณ ํ๋ฉด ์ค์ ๋์ ํธ์ถ๋ฉ๋๋ค {/im-trying-to-set-state-to-a-function-but-it-gets-called-instead/}
state์ ํจ์๋ฅผ ๋ฃ์ ์ ์์ต๋๋ค.
const [fn, setFn] = useState(someFunction);
function handleClick() {
setFn(someOtherFunction);
}
ํจ์๋ฅผ ๊ฐ์ผ๋ก ์ ๋ฌํ๋ฉด React๋ someFunction
์ ์ด๊ธฐํ ํจ์๋ก ์ฌ๊ธฐ๊ณ , someOtherFunction
์ ์
๋ฐ์ดํฐ ํจ์๋ผ๊ณ ์ฌ๊น๋๋ค. ๋ฐ๋ผ์ ์ด๋ค์ ํธ์ถํด์ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ ์ฅํ๋ ค๊ณ ์๋ํฉ๋๋ค. ์ ๋ง๋ก ํจ์๋ฅผ ์ ์ฅํ๊ธธ ์ํ๋ค๋ฉด, ๋ ๊ฒฝ์ฐ ๋ชจ๋ ํจ์ ์์ () =>
๋ฅผ ๋ฃ์ด์ผ ํฉ๋๋ค. ๊ทธ๋ฌ๋ฉด React๋ ์ ๋ฌํ ํจ์๋ฅผ ๊ฐ์ผ๋ก ์ ์ฅํฉ๋๋ค.
const [fn, setFn] = useState(() => someFunction);
function handleClick() {
setFn(() => someOtherFunction);
}