react - Learn React(Adding Interactivity)
๐ React
์ด ๊ธ์ ๋ฆฌ์กํธ ๊ณต์๋ฌธ์ - ํจ์ ์ปดํฌ๋ํธ๋ฅผ ์ฝ๊ณ ์์ฑํ ๊ธ์ ๋๋ค. ๋ชจ๋ ๋ด์ฉ์ ๋ค๋ฃจ์ง๋ ์๊ณ ๊ฐ์ธ์ ์ผ๋ก ๋ถ์กฑํ๋ค๊ณ ๋๊ผ๋ ๋ถ๋ถ, ์๋กญ๊ฒ ์๊ฒ ๋ ๋ถ๋ถ๋ค์ ๊ฐ๋จํ๊ฒ ์ ๋ฆฌํ ์์ ์ ๋๋ค.
Learn React - Adding Interactivity
๋ฆฌ์กํธ์์๋ ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ ๋ณํ๋ ๋ฐ์ดํฐ
๋ฅผ ๋ชจ๋ state
๋ผ๊ณ ํ๋ค.
Responding to events
React๋ฅผ ํตํด JSX์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ์ถ๊ฐํ ์ ์๋ค. ์ด๋, ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ํด๋ฆญ, ๋ง์ฐ์ค์ค, input์ ์ด์ ๋ง์ถ๊ธฐ ์ฌ์ฉ์ ์ํธ์์ฉ์ ์ํด ์คํ๋๋ ์ฝ๋ฐฑํจ์๋ฅผ ์๋ฏธํ๋ค.
State: a componentโs memory
์ปดํฌ๋ํธ๋ ์ํธ ์์ฉ์ ๊ฒฐ๊ณผ๋ก ํ๋ฉด์ ๋ด์ฉ์ ๋ณ๊ฒฝํด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค. ์ด ๋, ์ปดํฌ๋ํธ๋ ์ด๋ฐ ๋ณํ๋ ๋ด์ฉ์ ๊ธฐ์ต
ํ๊ณ ์์ด์ผ ํ๋ค. React์์๋ ์ด๋ฐ ์ข
๋ฅ์ ์ปดํฌ๋ํธ๋ณ ๋ฉ๋ชจ๋ฆฌ๋ฅผ state๋ผ๊ณ ๋ถ๋ฅธ๋ค.
์ปดํฌ๋ํธ์ state๋ฅผ ์ถ๊ฐํ๋ ค๋ฉด useState ํ ์ ์ฌ์ฉํ๋ฉด ๋๋ค. ํ ๋ค์ ์ปดํฌ๋ํธ๊ฐ React ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์๊ฒ ํด์ฃผ๋ ํน์ํ ํจ์๋ค์ด๋ค. useState ํ ์ ์ฌ์ฉํ๋ฉด state ๋ณ์๋ฅผ ์ ์ธํ ์ ์๋ค. ์ด๊ธฐ state๋ฅผ ๋ฐ์ ํ์ฌ state์ ์ด๋ฅผ ์ ๋ฐ์ดํธํ ์ ์๋ state ์ค์ ์ ํจ์์ ๊ฐ ์์ ๋ฐํํ๋ค.
Render and commit
์ปดํฌ๋ํธ๊ฐ ํ๋ฉด์ ํ์๋๊ธฐ ์ ์, ์ปดํฌ๋ํธ๋ค์ ๋ฆฌ์กํธ์์ ๋ ๋๋ง๋์ด์ผ ํ๋ค. ์๋์ ๊ณผ์ ์ ๊ฑฐ์น๋ค.
- ๋ ๋๋ง ๋ฐ๋
- ์ปดํฌ๋ํธ ๋ ๋๋ง
- DOM์ ์ปค๋ฐ
State as a snapshot
์ผ๋ฐ JavaScript ๋ณ์์ ๋ฌ๋ฆฌ React state๋ ์ค๋ ์ท์ฒ๋ผ ๋์ํ๋ค. state ๋ณ์๋ฅผ ์ค์ ํด๋ ์ด๋ฏธ ๊ฐ์ง๊ณ ์๋ state ๋ณ์๋ ๋ณ๊ฒฝ๋์ง ์๊ณ ๋์ ๋ฆฌ๋ ๋๋ง๋๋ค.
console.log(count); // 0
setCount(count + 1); // Request a re-render with 1
console.log(count); // Still 0!
Queueing a series of state updates
import { useState } from 'react';
export default function Counter() {
const [score, setScore] = useState(0);
function increment() {
setScore(score + 1);
}
return (
<>
<button onClick={() => increment()}>+1</button>
<button onClick={() => {
increment();
increment();
increment();
}}>+3</button>
<h1>Score: {score}</h1>
</>
)
}
// '+3' ๋ฒํผ์ ํด๋ฆญํด๋ ์ซ์๋ 1์ฉ๋ง ์ฆ๊ฐํ๋ค.
setState๋ฅผ ์คํํ๋ฉด ์๋ก์ด ๋ ๋๋ง์ ์์ฒญํ์ง๋ง ์ด๋ฏธ ์คํ ์ค์ธ ์ฝ๋์์๋ ๋ณ๊ฒฝํ์ง ์๋๋ค. ์ ๋ก์ง์ ์๋์ ๊ฐ์ด ์์ ํ๋ฉด ์ํ๋ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์๋ค.
import { useState } from 'react';
export default function Counter() {
const [score, setScore] = useState(0);
function increment() {
setScore(s => s + 1);
}
return (
<>
<button onClick={() => increment()}>+1</button>
<button onClick={() => {
increment();
increment();
increment();
}}>+3</button>
<h1>Score: {score}</h1>
</>
)
}
Updating objects in state
state์๋ ๊ฐ์ฒด๋ฅผ ํฌํจํ JS์์์ ๋ชจ๋ ๊ฐ
์ ํ ๋นํ ์ ์๋ค. state์ ๊ฐ์ฒด๋ฅผ ํ ๋นํ์ ๋๋, ํด๋น ๊ฐ์ฒด ๋ด๋ถ ๊ฐ์ ๋ณ๊ฒฝํด์๋ ์๋๋ค.
๋์ ๊ฐ์ฒด๋ฅผ ์
๋ฐ์ดํธํ๋ ค๋ฉด ์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ฑฐ๋ ๊ธฐ์กด ๊ฐ์ฒด์ ๋ณต์ฌ๋ณธ์ ๋ง๋ ๋ค์ ํด๋น ๋ณต์ฌ๋ณธ์ ์ฌ์ฉํ๋๋ก state๋ฅผ ์
๋ฐ์ดํธํด์ผ ํ๋ค.
์ผ๋ฐ์ ์ผ๋ก
...
์ ๊ฐ์ ์คํ๋ ๋ ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํด ๊ฐ์ฒด๋ ๋ฐฐ์ด์ ๋ณต์ฌํ์ฌ ์ฌ์ฉํ๋ค.
(์ถ๊ฐ) ๋ถ๋ณ์ฑ์ ์ ์งํ๋ฉด์ ๊ฐ์ฒด๋ฅผ ๋ณต์ฌํ๋ ๊ฒ์ด ์ซ๋ค๋ฉด Immer์ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ๋ณต๋๋ ์ฝ๋์ ์์ ์ค์ผ ์ ์๋ค.
Updating arrays in state
๊ฐ์ฒด์ ๋ง์ฐฌ๊ฐ์ง๋ก ๋ฐฐ์ด ๋ํ state์ ํ ๋นํ ํ, ๊ทธ state(๋ฐฐ์ด์ด ํ ๋น๋)์ ๋ด๋ถ๋ฅผ ์ง์ ๋ณ๊ฒฝํด์๋ ์๋๋ค. ์๋ก์ด ๋ฐฐ์ด๋ก ์ ๋ฐ์ดํธํ๊ธฐ ์ํด ์ ๋ฐฐ์ด ํน์ ๊ธฐ์กด ๋ฐฐ์ด์ ๋ณต์ฌ๋ณธ์ ์์ฑํ ํ setState๋ก ์ ๋ฐ์ดํธํ๋ค.
Responding to Events
Adding event handlers
๋ฆฌ์กํธ๋ jsx ๋ด๋ถ์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ์ถ๊ฐํ ์ ์๋ค.
- ์ด๋ฒคํธ ํธ๋ค๋ฌ ํจ์๋ ์ผ๋ฐ์ ์ผ๋ก ์ปดํฌ๋ํธ ์์ ์ ์๋๋ค.
- ์ด๋ฒคํธ ํธ๋ค๋ฌ ํจ์๋ ์ผ๋ฐ์ ์ผ๋ก
handleXXX
์ ๊ฐ์ ์ด๋ฆ์ ๊ฐ์ง๋ค.
export default function Button() {
function handleClick() {
alert('You clicked me!');
}
return (
<button onClick={handleClick}>
Click me
</button>
);
}
์ฃผ์! ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ์ ๋ฌํ๋ ๊ฒ์ ํธ์ถ๋ ํจ์(๋ฆฌํด๊ฐ)์ด ์๋ ํจ์ ์์ฒด์ด๋ค.
Passing event handlers as props
๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ ์์์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ์ง์ ํด์ผํ ๋๊ฐ ์๋ค. ์ด๋, ์์ ์ปดํฌ๋ํธ์๊ฒ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ์ ๋ฌํ๋ ๊ฒ์ด ์ข๋ค.
function Button({ onClick, children }) {
return (
<button onClick={onClick}>
{children}
</button>
);
}
function PlayButton({ movieName }) {
function handlePlayClick() {
alert(`Playing ${movieName}!`);
}
return (
<Button onClick={handlePlayClick}>
Play "{movieName}"
</Button>
);
}
function UploadButton() {
return (
<Button onClick={() => alert('Uploading!')}>
Upload Image
</Button>
);
}
export default function Toolbar() {
return (
<div>
<PlayButton movieName="Kiki's Delivery Service" />
<UploadButton />
</div>
);
}
- ์ด ๋ ์ปดํฌ๋ํธ์ props ๋ช
์
onXXX
๋ก ํ๊ณ ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ์ด๋ฆ์handleXXX
๋ก ํ๋ ๊ฒ์ด ๊ด๋ก์ด๋ค.
Naming event handler props
๊ด๋ก์ ์ด๋ฒคํธ ํธ๋ค๋ฌ props์ on์ผ๋ก ์์ํ๊ณ ๊ทธ ๋ค์ ๋๋ฌธ์๊ฐ ์์ผํ๋ค. ์๋ฅผ ๋ค์ด, onClick
, onSmash
์ ๊ฐ์ ์ด๋ฆ์ ๊ฐ์ง๋ค.
export default function App() {
return (
<Toolbar
onPlayMovie={() => alert('Playing!')}
onUploadImage={() => alert('Uploading!')}
/>
);
}
function Toolbar({ onPlayMovie, onUploadImage }) {
return (
<div>
<Button onClick={onPlayMovie}>
Play Movie
</Button>
<Button onClick={onUploadImage}>
Upload Image
</Button>
</div>
);
}
function Button({ onClick, children }) {
return (
<button onClick={onClick}>
{children}
</button>
);
}
App
์ปดํฌ๋ํธ๋ Toolbar
์ปดํฌ๋ํธ๊ฐ onPlayMovie
๋๋ onUploadImage
๋ก ์ด๋ค ์์
์ ์ํํ ์ง ์ ํ์๊ฐ ์๋ค.
์ฆ, ํด๋น ๋ถ๋ถ์ Toolbar
๊ฐ ๋งก์ ์ผ์ด๋ค.
์ด๋ฒคํธ ํธ๋ค๋ฌ์ ์ ์ ํ HTML ํ๊ทธ๋ฅผ ์ฌ์ฉํ๋๋ก ํ์! ex)
<div onClick={handleClick></div>
๋ ๊ฐ๋ฅํ์ง๋ง<button onClick={handleClick></button>
์ด ๋ ๋ช ํํ๋ค.
Event propagation
์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ์ปดํฌ๋ํธ์ ์์ ์ ์๋ ๋ชจ๋ ํ์ ์ปดํฌ๋ํธ์ ์ด๋ฒคํธ๋ ํฌ์ฐฉํ๋ค. ์ฆ, ๋ฒ๋ธ๋ง์ด ์ผ์ด๋๋ ๊ฒ์ด๋ค.
export default function Toolbar() {
return (
<div className="Toolbar" onClick={() => {
alert('You clicked on the toolbar!');
}}>
<button onClick={() => alert('Playing!')}>
Play Movie
</button>
<button onClick={() => alert('Uploading!')}>
Upload Image
</button>
</div>
);
}
- Toolbar๋ฅผ ํด๋ฆญํ๋ฉด
Playing!
์ด ๋จผ์ ์ถ๋ ฅ๋๊ณ , ๊ทธ ๋ค์์ Toolbar์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๊ฐ ์คํ๋๋ค.
JSX ํ๊ทธ์์๋ง ์๋ํ๋
onScroll
์ ์ ์ธํ ๋ชจ๋ ์ด๋ฒคํธ๋ ์ ํ๋๋ค.
Stopping propagation
์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ์ด๋ฒคํธ ๊ฐ์ฒด๋ฅผ ์ ์ผํ ์ธ์๋ก ๋ฐ๋๋ค. ๊ด๋ก์ ์ด๋ฒคํธ ๊ฐ์ฒด๋ e
๋๋ event
๋ผ๋ ์ด๋ฆ์ผ๋ก ์ฌ์ฉํ๋ค.
์ด ๋, e.stopPropagation()
์ ํธ์ถํ๋ฉด ์ด๋ฒคํธ ์ ํ๋ฅผ ๋ง์ ์ ์๋ค.
function Button({ onClick, children }) {
return (
<button onClick={e => {
e.stopPropagation();
onClick();
}}>
{children}
</button>
);
}
export default function Toolbar() {
return (
<div className="Toolbar" onClick={() => {
alert('You clicked on the toolbar!');
}}>
<Button onClick={() => alert('Playing!')}>
Play Movie
</Button>
<Button onClick={() => alert('Uploading!')}>
Upload Image
</Button>
</div>
);
}
Capture phase events
- ๋ฆฌ์กํธ์ ์ด๋ฒคํธ๋ฅผ ๋ฒ๋ธ๋ง์ด ์๋ ์บก์ฒ๋ง์ผ๋ก ์ฒ๋ฆฌํ๊ณ ์ถ๋ค๋ฉด,
onXXXCapture
๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
Preventing default behavior
์ผ๋ถ ๋ธ๋ผ์ฐ์ ์ด๋ฒคํธ์๋ ์ฐ๊ฒฐ๋ ๊ธฐ๋ณธ ๋์์ด ์๋ค. ex) <form>
์ onSubmit
์ ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจํ๋ค.
์ด ๋, e.preventDefault()
๋ฅผ ํธ์ถํ๋ฉด ๊ธฐ๋ณธ ๋์์ ๋ง์ ์ ์๋ค.
export default function Signup() {
return (
<form onSubmit={() => alert('Submitting!')}>
<input />
<button>Send</button>
</form>
);
}
e.stopPropagation(): ์ด๋ฒคํธ์ ์ ํ๋ฅผ ๋ง๋๋ค.
e.preventDefault(): ๋ช ๊ฐ์ง ์ด๋ฒคํธ์ ๋ํ ๋ธ๋ผ์ฐ์ ์ ๊ธฐ๋ณธ ๋์์ ๋ง๋๋ค.
Can event handlers have side effects?
์ด๋ฒคํธ ํธ๋ค๋ฌ์์๋ ์๋ง์ side effect๊ฐ ๋ฐ์ํ ์ ์๋ค.
๋ ๋๋ง ํจ์(์ปดํฌ๋ํธ)์ ๋ฌ๋ฆฌ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ์์ํ ํ์๊ฐ ์๋ค! ํ์ง๋ง ์ด๋ฐ ์ด๋ฒคํธ์ ๋ฐ๋ผ ์ผ๋ถ ์ ๋ณด๋ฅผ ๋ณ๊ฒฝํ๋ ค๋ฉด ๋จผ์
์ ๋ณด๋ฅผ ์ ์ฅํ ๋ฐฉ๋ฒ์ด ํ์ํ๋ค. ์ด ๋, state
๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
State: A Componentโs Memory
์ปดํฌ๋ํธ๋ ์ํธ ์์ฉ์ ๊ฒฐ๊ณผ๋ก ํ๋ฉด์ ๋ด์ฉ์ ๋ณ๊ฒฝํ๋ค. ์ด ๋, ์ปดํฌ๋ํธ๋ ์ด์ ์ ์ด๋ค ์ํธ ์์ฉ์ด ์์๋์ง ๊ธฐ์ตํด์ผ ํ๋ค.
์ด๋ฐ ์ข
๋ฅ์ ์ปดํฌ๋ํธ ๋ณ ๋ฉ๋ชจ๋ฆฌ๋ฅผ state
๋ผ๊ณ ๋ถ๋ฅธ๋ค.
When a regular variable isnโt enough
import { sculptureList } from './data.js';
export default function Gallery() {
let index = 0;
function handleClick() {
index = index + 1;
}
let sculpture = sculptureList[index];
return (
<>
<button onClick={handleClick}>
Next
</button>
<h2>
<i>{sculpture.name} </i>
by {sculpture.artist}
</h2>
<h3>
({index + 1} of {sculptureList.length})
</h3>
<img
src={sculpture.url}
alt={sculpture.alt}
/>
<p>
{sculpture.description}
</p>
</>
);
}
์ ์์ ์์ ๋ฒํผ์ ํด๋ฆญํด๋ ํ๋ฉด์ด ๋ณํ์ง ์๋๋ค.
- ์ง์ญ ๋ณ์๋ ๋ ๋๋ง ๊ฐ์ ์ ์ง๋์ง ์๋๋ค. React๊ฐ ์ด ์ปดํฌ๋ํธ๋ฅผ ๋๋ฒ์งธ๋ก ๋ ๋๋งํ ๋ ์ง์ญ ๋ณ์์ ๋ํ ๋ณ๊ฒฝ ์ฌํญ์ ๊ณ ๋ คํ์ง ์๋๋ค.
- ์ง์ญ ๋ณ์๋ฅผ ๋ณ๊ฒฝํด๋ ๋ ๋๋ง์ ๋ฐ๋์ํค์ง ์๋๋ค. ๋จ์ ์ง์ญ๋ณ์์ ๋ณ๊ฒฝ์ผ๋ก React๋ ์ปดํฌ๋ํธ๋ฅผ ๋ค์ ๋ ๋๋งํ ๊ฒ์ ์ธ์งํ์ง ๋ชปํ๋ค.
์ด ๋, ์ปดํฌ๋ํธ๋ฅผ ์ ๋ฐ์ดํฐ๋ก ์ ๋ฐ์ดํธํ๋ ค๋ฉด 2๊ฐ์ง์ ์์ ์ด ํ์ํ๋ค.
- ๋ ๋๋ง ์ฌ์ด, ๋ฐ์ดํฐ๋ฅผ ์ ์งํด์ผํ๋ค.
- ์๋ก์ด ๋ฐ์ดํฐ๋ก ์ปดํฌ๋ํธ๋ฅผ ๋ค์ ๋ ๋๋งํด์ผ ํ๋ค.
์์ 2๊ฐ์ง๋ฅผ ์ ๊ณตํ๋ ๊ฒ์ด useState
ํ
์ด๋ค.
Meet your first Hook
React์์๋ use
๋ก ์์ํ๋ ํจ์๋ฅผ ํ
(hook)์ด๋ผ๊ณ ๋ถ๋ฅธ๋ค.
ํ
์ ๋ ๋๋ง ์ค์ผ ๋๋ง ์ฌ์ฉํ ์ ์๋ ํน๋ณํ ํจ์์ด๋ค.
ํ ์ ์ปดํฌ๋ํธ์ ์ต์์ ๋ ๋ฒจ ํน์ ์ปค์คํ ํ ์์๋ง ํธ์ถํ ์ ์๋ค.
ํ ์ ํจ์์ด์ง๋ง ์ปดํฌ๋ํธ์ ํ์์ ๋ํ ๋ฌด์กฐ๊ฑด์ ์ธ ์ ์ธ์ผ๋ก ์๊ฐํ๋ฉด ํธํ๋ค.
ํ์ผ ์๋จ์์ ๋ชจ๋์import
ํ๋ ๊ฒ๊ณผ ์ ์ฌํ๊ฒ ์ปดํฌ๋ํธ ์๋จ์์ React ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ค.
Anatomy of useState
useState
๋ฅผ ํธ์ถํ๋ค๋ ๊ฑด, React ์ปดํฌ๋ํธ๊ฐ ๋ฌด์ธ๊ฐ๋ฅผ ๊ธฐ์ตํ๋๋ก ์์ฒญํ๋ ๊ฒ์ด๋ค.
import { useState } from 'react';
const [index, setIndex] = useState(0);
์์ ๊ฒฝ์ฐ, React๊ฐ index
๋ฅผ ๊ธฐ์ตํ๊ธฐ๋ฅผ ์ํ๋ ๊ฒ์ด๋ค.
์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง๋ ๋๋ง๋ค useState
๋ 2๊ฐ์ ๊ฐ์ ํฌํจํ๋ ๋ฐฐ์ด์ ์ ๊ณตํ๋ค.
- ์ ์ฅํ ๊ฐ์ ๊ฐ์ง state ๋ณ์
- state ๋ณ์๋ฅผ ์ ๋ฐ์ดํธ ๋ฐ React๊ฐ ์ปดํฌ๋ํธ๋ฅผ ๋ค์ ๋ ๋๋งํ๋๋ก ํธ๋ฆฌ๊ฑฐํ ์ ์๋ setter ํจ์
useState์ ๋์ ์๋ฆฌ!
์์ index state๋ฅผ ์์ ๋ก ๋ค๋๋ก ํด๋ณด์
- ์ปดํฌ๋ํธ๊ฐ ์ฒ์ ๋ ๋๋ง๋๋ค. index์ ์ด๊ธฐ๊ฐ์ผ๋ก 0์ useState์ ์ ๋ฌ๋์์ผ๋ฏ๋ก
[0, setIndex]
๊ฐ ๋ฐํ๋๋ค. ์ด ๋, React๋ 0์ ์ต์ state๋ก ๊ธฐ์ตํ๋ค. - state๋ฅผ ์ ๋ฐ์ดํธํ๋ค. react๋ ์ด์ 1์ ์ต์ state๋ก ๊ธฐ์ตํ๋ค.
- ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง๋๋ค. ๋ฌผ๋ก ์ฌ์ ํ useState(0)์ธ ์ฝ๋๊ฐ ์์ง๋ง, index๋ฅผ 1๋ก ์ค์ ํ ๊ฒ์ ๊ธฐ์ตํด์ [1, setIndex]๋ฅผ ๋ฐํํ๋ค.
Giving a component multiple state variables
ํ๋์ ์ปดํฌ๋ํธ์ ์ฌ๋ฌ๊ฐ์ state ๋ณ์๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ์ด ๋, ์๋ก ์ฐ๊ด์ด ์๋(๋๊ฐ์ state ๋ณ์๋ฅผ ์์ฃผ ํจ๊ป ๋ณ๊ฒฝํ๋) ๊ฒฝ์ฐ ํ๋์ state ๋ณ์๋ก ๊ด๋ฆฌํ๋ ๊ฒ์ด ์ข๋ค.
์ด๋ค state๋ฅผ ๋ฐํํ ์ง React๋ ์ด๋ป๊ฒ ์ ์ ์์๊น?
useState
ํธ์ถ์ด ์ด๋ค state ๋ณ์๋ฅผ ์ฐธ์กฐํ๋์ง์ ๋ํ ์ ๋ณด๋ฅผ ๋ฐ์ง ๋ชปํ๋ค. ์ด๋ฐ โ์๋ณ์โ๊ฐ ์๋๋ฐ ์ด๋ค state ๋ณ์๋ฅผ ๋ฐํํ ์ง ์ด๋ป๊ฒ ์ ์ ์์๊น? ํ ์๋์ผํ ์ปดํฌ๋ํธ์ ๋ชจ๋ ๋ ๋๋ง์์ ์์ ์ ์ด๊ณ ๋์ผํ ์์๋ก ํธ์ถ๋์ด์ผ ํ๋ค.
์ฆ, ํ ์ ํญ์ ๊ฐ์ ์์๋ก ํธ์ถ๋์ด์ผ ํ๊ธฐ ๋๋ฌธ์ ์ต์์ ๊ณ์ธต์์ ํธ์ถ๋์ด์ผ ํ๋ ๊ฒ์ด๋ค.(eslint-plugin-react-hooks์ด ์ด๋ฅผ ์ ์ก์์ค๋ค.)
React๋ ๋ด๋ถ์ ์ผ๋ก ๋ชจ๋ ์ปดํฌ๋ํธ์ ๋ํ ํ ์์ state์ ๋ฐฐ์ด์ ๊ฐ๋๋ค. ๋ํ ๋ ๋๋ง ์ ์
0
์ผ๋ก ์ค์ ๋ ํ์ฌ ์ ์ธ๋ฑ์ค๋ฅผ ์ ์งํ๋ค.useState
๋ฅผ ํธ์ถํ ๋๋ง๋ค React๋ ๋ค์ state ์์ ์ ๊ณตํ๋ฉด์ index๋ฅผ 1 ์ฆ๊ฐ์ํจ๋ค.
let componentHooks = [];
let currentHookIndex = 0;
// How useState works inside React (simplified).
function useState(initialState) {
let pair = componentHooks[currentHookIndex];
if (pair) {
// This is not the first render,
// so the state pair already exists.
// Return it and prepare for next Hook call.
currentHookIndex++;
return pair;
}
// This is the first time we're rendering,
// so create a state pair and store it.
pair = [initialState, setState];
function setState(nextState) {
// When the user requests a state change,
// put the new value into the pair.
pair[0] = nextState;
updateDOM();
}
// Store the pair for future renders
// and prepare for the next Hook call.
componentHooks[currentHookIndex] = pair;
currentHookIndex++;
return pair;
}
์ฆ, [[state1, setState1], [state2, setState2], ...]
์ด๋ฐ์์ผ๋ก ์ ์ฅ๋์ด์๋ ๊ฒ์ด๋ค.
State is isolated and private
state๋ ์ปดํฌ๋ํธ์ ์ํด ์์ ํ ์บก์ํ๋๋ค. ์ฆ, state๋ ์ปดํฌ๋ํธ ์ธ๋ถ์์ ์ ๊ทผํ ์ ์๋ค.
์ด ๋ถ๋ถ์ด ๋ฐ๋ก ๋ชจ๋ ์๋จ์ ์ ์ธํ๋ ์ผ๋ฐ ๋ณ์
์ state
์ ์ฐจ์ด์ ์ด๋ค. state๋ ํน์ ํจ์ ํธ์ถ์ ๋ฌถ์ด์ง ์๊ณ , ์ฝ๋์ ํน์ ์์น์๋ ๋ฌถ์ด์ง ์์ผ๋ฉด์ ํ๋ฉด์์ ํน์ ์์น์ ์ง์ญ์ ์ด๋ค.
๋ํ, state๋ ์ด๋ฅผ ์ ์ธํ ์ปดํฌ๋ํธ ์ธ์๋ ์์ ํ ๋น๊ณต๊ฐ๋๊ณ , ๋ถ๋ชจ ์ปดํฌ๋ํธ๋ ์ด๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ค.
๋ง์ฝ state๋ฅผ ๋๊ฐ์ ์ปดํฌ๋ํธ๊ฐ ๊ณต์ ํ๊ณ ๋๊ธฐํํ๋ ค๋ฉด ๊ฐ์ฅ ๊ฐ๊น์ด ๋ถ๋ชจ ์ปดํฌ๋ํธ์ ๋์ด props๋ก ์ ๋ฌํด์ผ ํ๋ค.
Render and Commit
์ปดํฌ๋ํธ๋ฅผ ํ๋ฉด์ ํ์ํ๊ธฐ ์ ์ React์์ ๋ ๋๋ง์ ํด์ผ ํ๋ค.
๋ฆฌ์กํธ๊ฐ ๊ณ ๊ฐ๋ค์ ์์ฒญ์ ๋ฐ๊ณ ์ฃผ๋ฌธ์ ๊ฐ์ ธ์ค๋ ์จ์ดํฐ๋ผ๊ณ ์๊ฐํด๋ณด์. ๊ทธ๋ฆฌ๊ณ ์ฃผ๋ฐฉ์์๋ ์ปดํฌ๋ํธ๋ฅผ ์ฌ๋ฃ๋ก ์๋ฆฌ๋ฅผ ํ๊ณ ์๋ค. UI๋ฅผ ์์ฒญํ๊ณ ์๋นํ๋ ๊ณผ์ ์ ์๋ 3๋จ๊ณ๋ก ์ด๋ฃจ์ด์ง๋ค.
- Triggering a render: ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง์ ์์ํ๋๋ก ํธ๋ฆฌ๊ฑฐํ๋ค. => ์๋์ ์ฃผ๋ฌธ์ ์ฃผ๋ฐฉ์ผ๋ก ์ ๋ฌํ๋ค.
- Rendering the component: ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง์ ์ํํ๋ค. => ์ฃผ๋ฐฉ ์ฃผ๋ฌธ์ ๋ฐ์ ์๋ฆฌํ๋ค.
- Committing to the DOM: DOM์ ์ปค๋ฐํ๋ค. => ์๋์๊ฒ ์๋ฆฌ๋ฅผ ๋ด๋๋๋ค.
Step 1: Trigger a render
์ปดํฌ๋ํธ์ ๋ ๋๋ง์ด ์ผ์ด๋๋ ๋ฐ์๋ 2๊ฐ์ง์ ์ด์ ๊ฐ ์๋ค.
- ์ปดํฌ๋ํธ๊ฐ ์ฒ์์ผ๋ก ๋ ๋๋ง๋๋ ๊ฒฝ์ฐ
์ฑ์ ์์ํ๊ธฐ ์ํด์๋ ์ฒซ ๋ ๋๋ง์ ํด์ผ ํ๋ค.
import Image from './Image.js';
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'))
root.render(<Image />); // ์ฒซ ๋ฒ์งธ ๋ ๋๋ง
- ์ปดํฌ๋ํธ์ state๋ props(๋ถ๋ชจ ์ปดํฌ๋ํธ๋ฅผ ํตํด ์ ๋ฌ๋ state)๊ฐ ๋ณ๊ฒฝ๋์ด ๋ค์ ๋ ๋๋ง๋๋ ๊ฒฝ์ฐ
setState
๋ก state๋ฅผ ๋ณ๊ฒฝํ์ฌ ์ถ๊ฐ๋ก ๋ ๋๋งํ ์ ์๋ค. ์ปดํฌ๋ํธ์ state๋ฅผ ์ ๋ฐ์ดํธํ๋ฉด ์๋์ผ๋ก ๋ ๋๋ง์ด ๋๊ธฐ์ด์ ์ถ๊ฐ๋๋ค. (์๋น์์ ์๋์ด ์ฒซ ์ฃผ๋ฌธ ์ดํ์ ์ถ๊ฐ ์ฃผ๋ฌธ์ ํ๋ ๊ฒ๊ณผ ๊ฐ๋ค.)
Step 2: React renders your components
๋ ๋๋ง์ triggerํ๋ฉด, ๋ฆฌ์กํธ๋ ํจ์ ์ปดํฌ๋ํธ๋ฅผ ํธ์ถํ์ฌ ํ๋ฉด์ ํ์ํ ๋ด์ฉ์ ํ์
ํ๋ค. ์ฆ, ๋ ๋๋ง
์ ๋ฆฌ์กํธ์์ ํจ์ ์ปดํฌ๋ํธ๋ฅผ ํธ์ถํ๋ ๊ฒ์ด๋ค.
- ์ฒซ ๋ ๋๋ง์์๋ ๋ฃจํธ ์ปดํฌ๋ํธ๋ฅผ ํธ์ถํ๋ค.
- ์ดํ ๋ ๋๋ง์์๋ state์ ์ ๋ฐ์ดํธ์ ์ํด ๋ ๋๋ง์ด ๋ฐ๋๋ ํจ์ ์ปดํฌ๋ํธ๋ฅผ ํธ์ถํ๋ค.
์์ ๊ณผ์ ์์ฒด๋ ์ฌ๊ท์
์ผ๋ก ๋์ํ๋ค. ์
๋ฐ์ดํธ๋ ์ปดํฌ๋ํธ๊ฐ ๋ค๋ฅธ ์ปดํฌ๋ํธ(์์ ์ปดํฌ๋ํธ)๋ฅผ ๋ฒํํ๋ฉด ๋ค์์ผ๋ก ํด๋น ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ๊ณ ๋ ๊ทธ ์ปดํฌ๋ํธ๊ฐ ๋ค๋ฅธ
์ปดํฌ๋ํธ๋ฅผ ๋ฐํํ๋ฉด ํด๋น ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ๋ค. ์ค์ฒฉ๋ ์ปดํฌ๋ํธ๊ฐ ๋์ด์ ์กด์ฌํ์ง ์๊ณ ํ๋ฉด์ ํ์๋์ด์ผ ํ๋ ๋ด์ฉ์ ์ ํํ ์ ๋๊น์ง ๊ณ์๋๋ค.
๋ ๋๋ง์ ํญ์ ์์ ๊ณ์ฐ์ด์ด์ผ ํ๋ค.
- ๋์ผํ ์ ๋ ฅ์ ๋ํด์ ๋์ผํ ์ถ๋ ฅ์ด ๋ฐํ๋์ด์ผ ํ๋ค.
- ํจ์ ์ธ๋ถ์ ์ด๋ค ๊ฒ๋ ๋ณ๊ฒฝํด์๋ ์๋๋ค.
Optimizing performance
์ ๋ฐ์ดํธ๋ ์ปดํฌ๋ํธ ๋ด์ ์๋ ๋ชจ๋ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ๋ ํ์๋ ๋นํจ์จ์ ์ด๋ค.(ํนํ ์ ๋ฐ์ดํธ๋ ์ปดํฌ๋ํธ๊ฐ ์์์ ์์์๋ก!)
์ฑ๊ธํ๊ฒ ์ต์ ํํ์ง ์๋๋ก ํด๋ผ!
Step 3: React commits changes to the DOM
๋ฆฌ์กํธ๋ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋ง(ํธ์ถ)ํ ํ, DOM์ ์์ ํ๋ค.
- ์ฒซ ๋ ๋๋ง์ ๊ฒฝ์ฐ, ๋ฆฌ์กํธ๋
appendChild()
DOM api๋ฅผ ์ฌ์ฉํ์ฌ ์์ฑํ ๋ชจ๋ DOM ๋ ธ๋๋ฅผ ํ๋ฉด์ ํ์ํ๋ค. - ์ดํ ๋ ๋๋ง(๋ฆฌ๋ ๋๋ง)์ ๊ฒฝ์ฐ, ๋ฆฌ์กํธ๋ ํ์ํ ์ต์ํ์ ์์ (๋ ๋๋ง ์ค ๊ณ์ฐ๋ ์์ )์ ์ ์ฉํ์ฌ DOM์ด ์ต์ ๋ ๋๋ง ์ถ๋ ฅ๊ณผ ์ผ์น์ํจ๋ค.
๋ฆฌ์กํธ๋ ๋ ๋๋ง ์ฌ์ด์ ์ฐจ์ด๊ฐ ์๋ ๊ฒฝ์ฐ์๋ง DOM ๋ ธ๋๋ฅผ ๋ณ๊ฒฝํ๋ค. ์๋์ ๊ฐ์ด ๋งค์ด๋ง๋ค time์ด ๋ณ๊ฒฝ๋ ๋(๋ฆฌ๋ ๋๋ง์ด ๋ฐ์), input์ ๋ด๋ถ์ ์๋ value state๋ ๊ทธ ๊ฐ์ด ์ฌ๋ผ์ง์ง ์๊ณ ์ ์ง๋๋ค.
export default function Clock({ time }) {
return (
<>
<h1>{time}</h1>
<input />
</>
);
}
Epilogue: Browser paint
๋ ๋๋ง์ด ์๋ฃ๋๊ณ ๋ฆฌ์กํธ๊ฐ DOM์ ์
๋ฐ์ดํธํ ํ, ๋ธ๋ผ์ฐ์ ๋ ํ๋ฉด์ ๋ค์ ๊ทธ๋ฆฐ๋ค. ์ด ๋ถ๋ถ์ ๋ธ๋ผ์ฐ์ ๋ ๋๋ง
์ด๋ผ๊ณ ํ์ง๋ง, ํ์ฌ ๊ณต์๋ฌธ์์์๋ ํผ๋์ ํผํ๊ณ ์ ํ์ธํ
์ด๋ผ๊ณ ๋ถ๋ฅธ๋ค.
State as a Snapshot
state๋ ์ค๋ ์ท์ฒ๋ผ ๋์ํ๋ค. state ๋ณ์๋ฅผ ์ค์ ํด๋ ์ด๋ฏธ ๊ฐ์ง๊ณ ์๋ state ๋ณ์๋ ๋ณ๊ฒฝ๋์ง ์๊ณ , ๊ทธ ๋์ ๋ฆฌ๋ ๋๋ง์ด ์คํ๋๋ค.
import { useState } from 'react';
export default function Form() {
const [isSent, setIsSent] = useState(false);
const [message, setMessage] = useState('Hi!');
if (isSent) {
return <h1>Your message is on its way!</h1>
}
return (
<form onSubmit={(e) => {
e.preventDefault();
setIsSent(true);
sendMessage(message);
}}>
<textarea
placeholder="Message"
value={message}
onChange={e => setMessage(e.target.value)}
/>
<button type="submit">Send</button>
</form>
);
}
function sendMessage(message) {
// ...
}
- onSubmit ์ด๋ฒคํธ ํธ๋ค๋ฌ๊ฐ ์คํ๋๋ค.
- setIsSent(true)๊ฐ isSent๋ฅผ true๋ก ์ค์ ํ๊ณ ์ ๋ ๋๋ง์ ํ์ ๋๊ธฐ์ํจ๋ค..
- ๋ฆฌ์กํธ๋ isSent ๊ฐ์ ๋ฐ๋ผ์ ์ปดํฌ๋ํธ๋ฅผ ๋ค์ ๋ ๋๋งํ๋ค.
Rendering takes a snapshot in time
๋ ๋๋ง
์ด๋ ๋ฆฌ์กํธ๊ฐ ์ปดํฌ๋ํธ, ์ฆ ํจ์๋ฅผ ํธ์ถํ๋ค๋ ์๋ฏธ์ด๋ค. ํด๋น ํจ์์์ ๋ฐํํ๋ JSX๋ ๊ทธ ์๊ฐ์ UI ์ค๋
์ท๊ณผ ๊ฐ๋ค.
React๊ฐ ์ปดํฌ๋ํธ๋ฅผ ๋ฆฌ๋ ๋๋งํ ๋,
- ๋ฆฌ์กํธ๊ฐ ํจ์๋ฅผ ๋ค์ ํธ์ถํ๋ค.
- ํจ์๊ฐ ์๋ก์ด JSX ์ค๋ ์ท์ ๋ฐํํ๋ค.
- ์ดํ, ๋ฆฌ์กํธ๋ ์ด์ ์ค๋ ์ท๊ณผ ์ ์ค๋ ์ท์ ๋น๊ตํ๊ณ ์ผ์นํ๋๋ก ํ๋ฉด์ ์ ๋ฐ์ดํธํ๋ค.
์ปดํฌ๋ํธ์ ๋ฉ๋ชจ๋ฆฌ๋ก์ state๋ ํจ์๊ฐ ๋ฐํ๋ ํ ์ฌ๋ผ์ง๋ ์ผ๋ฐ ๋ณ์์๋ ๋ค๋ฅด๋ค. state๋ ํจ์ ์ธ๋ถ์ ์กด์ฌํ๋ ๋ณ์์ฒ๋ผ React ์์ฒด์ ์กด์ฌํ๋ค.
์๋์ ์ฝ๋๋ฅผ ๋ณด๋ฉด ๋ฒํผ์ 1๋ฒ ํด๋ฆญํ ๋๋ง๋ค, number๋ 3์ฉ ์ฆ๊ฐํ ๊ฒ ๊ฐ๋ค. ํ์ง๋ง number๋ 1์ฉ ์ฆ๊ฐํ๋ค.
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 1);
setNumber(number + 1);
console.log(number);
setNumber(number + 1);
}}>+3</button>
</>
)
}
setState
๋ฅผ ์คํํ๋ฉด ์ด์ ๋ ๋๋ง
์ ๋ํด์๋ง state๊ฐ ๋ณ๊ฒฝ๋๋ค. ์ฆ, ์๋ฌด๋ฆฌ setNumber๋ฅผ ํธ์ถํ์ด๋ ์ด์ ๋ ๋๋ง์์ number๋ 0์ด๊ธฐ ๋๋ฌธ์ 1๋ง ์ฆ๊ฐํ๋ ๊ฒ์ด๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ค๊ฐ์ console.log๋ฅผ ์ฐ์ด๋ณด๋ฉด 0์ด ์ฐํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
State over time
์๋ ์ฝ๋๋ฅผ ์คํํด๋ณด๋ฉด 3์ด ๋ค alert ๊ฐ์ผ๋ก๋ 0์ด ์ฐํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 5);
setTimeout(() => {
alert(number);
}, 3000);
}}>+5</button>
</>
)
}
์ ๊ทธ๋ด๊น? ์ด๋ ์ฌ์ฉ์๊ฐ ๋ฒํผ์ ํด๋ฆญํ๋ ์์ ์์ number๋ 0์ด๊ธฐ ๋๋ฌธ์ด๋ค. setTimeout์ 3์ด ๋ค์ ์คํ๋๊ธฐ ๋๋ฌธ์, 3์ด ๋ค์ ์คํ๋๋ alert์์๋ number๊ฐ 0์ด๋ค.(3์ด ๋ค์ number๊ฐ 0์ธ ์ค๋
์ท์ด ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ด๋ค.)
๋ฆฌ์กํธ๋ ํ๋์ ๋ ๋๋ง ์ด๋ฒคํธ ํธ๋ค๋ฌ ๋ด์์ state๊ฐ์ ๊ณ ์
์ผ๋ก ์ ์งํ๋ค. ์ฆ, ์ฝ๋๊ฐ ์คํ๋๋ ๋์ state๊ฐ ๋ณ๊ฒฝ๋์๋์ง ๊ฑฑ์ ํ ํ์๊ฐ ์๋ค.
Queueing a Series of State Updates
state ๋ณ์๋ฅผ ์ค์ ํ๋ฉด ๋ค์ ๋ ๋๋ง์ด ํ์ ๋ค์ด๊ฐ๋ค. ๊ทธ๋ฌ๋ ๊ฒฝ์ฐ์ ๋ฐ๋ผ์ ๋ค์ ๋ ๋๋ง์ ํ์ ์ ๋ฌํ๊ธฐ ์ ์, state์ ๋ํด ์ฌ๋ฌ ์์ ์ ์ํํ๊ณ ์ถ์ ์ ์๋ค.
React batches state updates
๋ฆฌ์กํธ๋ state ์
๋ฐ์ดํธ๋ฅผ ํ๊ธฐ ์ ์ ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ๋ชจ๋ ์ฝ๋๊ฐ ์คํ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฐ๋ค. ๋๋ฌธ์ ๋ฆฌ๋ ๋๋ง์ ๋ชจ๋ setState
ํธ์ถ์ด ์๋ฃ๋ ์ดํ์๋ง ์ผ์ด๋๋ค.
์ด๋ ์์์ ์์ ์ฃผ๋ฌธ์ ๋ฐ๋ ์จ์ดํฐ์ ๊ฐ๋ค. ์ฐ๋ฆฌ๊ฐ a,b,c๋ผ๋ ์์์ ์ฃผ๋ฌธํ๋ค๋ฉด ์จ์ดํฐ๋ ์ฐ๋ฆฌ๊ฐ a๋ฅผ ๋งํ์๋ง์ ์ฃผ๋ฌธ์ ์ ๋ฌํ์ง ์๋๋ค. ๋์ ์ ์ฐ๋ฆฌ๊ฐ ์ฃผ๋ฌธ์ ์๋ฃํ ๋๊น์ง ๊ธฐ๋ค๋ฆฐ๋ค. ๊ทธ๋ฆฌ๊ณ ์ฐ๋ฆฌ๊ฐ ์ฃผ๋ฌธ์ ์๋ฃํ๋ฉด ์จ์ดํฐ๋ ์ฃผ๋ฌธ์ ์ ๋ฌํ๋ค.
์ฌ์ง์ด ๋ค๋ฅธ ํ
์ด๋ธ์ ์ฃผ๋ฌธ๊น์ง ํ๋ฒ์ ๋ฐ์์ ์ ๋ฌํ๋ค.
์ด๋ ๊ฒ ํ๋ฉด ์ฌ๋ฌ ์ปดํฌ๋ํธ์์ ๋์จ ์ฌ๋ฌ state ๋ณ์๋ฅผ ์ ๋ฐ์ดํธํจ์ ๋ฐ๋ผ ๋ฆฌ๋ ๋๋ง์ triggerํ์ง ์์ ์ ์๋ค. ์ด ๋ง์ ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ๊ทธ ๋ด๋ถ์ ์๋ ์ฝ๋๊ฐ ์๋ฃ๋ ๋๊น์ง UI๊ฐ ์ ๋ฐ์ดํธ๋์ง ์๋๋ค๋ ๊ฒ์ด๋ค.
๋ฐ๋ฉด ๋ฆฌ์กํธ๋ ํด๋ฆญ๊ณผ ๊ฐ์ ์ฌ๋ฌ ์๋์ ์ธ ์ด๋ฒคํธ์ ๋ํด ์ผ๊ด ์ฒ๋ฆฌ ํ์ง ์๋๋ค. ๊ฐ ์ด๋ฒคํธ๋ ๊ฐ๋ณ์ ์ผ๋ก ์ฒ๋ฆฌ ๋๋ค.
Updating the same state multiple times before the next render
๋ค์ ๋ ๋๋ง ์ ์ ๋์ผํ state ๋ณ์๋ฅผ ์ฌ๋ฌ๋ฒ ์
๋ฐ์ดํธ ํ๊ณ ์ถ์ ๋, setState((prev) => prev + 1)
์ ๊ฐ์ด ํจ์๋ฅผ ์ธ์๋ก ๋๊ฒจ์ฃผ๋ฉด ๋๋ค.
์ด๋ ์ด์ ๋ ๋๋ง์์์ state๋ฅผ ์ฐธ์กฐํ๋ ๊ฒ์ด ์๋ state ํ์ ์ด์ state๋ฅผ ์ฐธ์กฐํ๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฅํ๋ค.
์ฐธ๊ณ : setState(5) ๋ํ setState(prev => 5)์ฒ๋ผ ๋์ํ๋ค.
Naming conventions
์ ๋ฐ์ดํฐ ํจ์ ์ธ์(์ฝ๋ฐฑ ํจ์)์ ์ด๋ฆ์ ํด๋น state ๋ณ์์ ์ฒซ ๊ธ์๋ก ์ง์ ํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ด๋ค.
setEnabled(e => !e);
setLastName(ln => ln.reverse());
setFriendCount(fc => fc * 2);