You will learn
- ๋ฆฌ์กํธ๊ฐ ์ธ์ state๋ฅผ ๋ณด์กดํ๊ณ ์ธ์ ์ด๊ธฐํ ํ๋์ง
- ๋ฆฌ์กํธ๊ฐ ๊ฐ์ ๋ก ์ปดํฌ๋ํธ์ state๋ฅผ ์ด๊ธฐํ ํ๊ฒ ํ๋ ๋ฐฉ๋ฒ
- key๊ฐ๊ณผ ํ์ ์ด ์ด๋ป๊ฒ state์ ๋ณด์กด ์ฌ๋ถ์ ์ํฅ์ ๋ผ์น๋์ง
state๋ ๋ ๋ ํธ๋ฆฌ ์์ ์๋ค
๋ฆฌ์กํธ๋ ์ปดํฌ๋ํธ ๊ตฌ์กฐ๋ฅผ ๋ ๋ ํธ๋ฆฌ ๋ผ๋ ํธ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋ง๋ ๋ค. ํํ ์ปดํฌ๋ํธ ์์์ state๋ฅผ ์ ์ธํ๋๊น state๊ฐ ์ปดํฌ๋ํธ ์์ ์์นํ๊ณ ์๋ค๊ณ ์๊ฐํ ์ ์์ผ๋ (ํธ์์ ๊ทธ๋ ๊ฒ ์๊ธฐํ๊ธฐ๋ ํ์ง๋ง), ์ฌ์ค state๋ ์ปดํฌ๋ํธ๊ฐ ์๋ ๋ฆฌ์กํธ์ ์๋ค.
์ปดํฌ๋ํธ๋ ์ฌ์ค ๋ ๋๋ง ๋ก์ง์ด๊ณ , ๋ ๋๋ง์ด ์คํ ๋ ๋ ํ์ํ ์ ๋ณด๋ค์ ๋ฆฌ์กํธ๊ฐ ๊ฐ์ง๊ณ ์๋ค๊ฐ ์ ๊ณตํ๋ ๊ฒ์ด๋ค. setState ํจ์ ํธ์ถ๋ก state ์
๋ฐ์ดํธ๊ฐ ๋ฐ์ํ๋ฉด ๋ฆฌ์กํธ๋ ํด๋น state๋ฅผ ๋ ๋ ํธ๋ฆฌ ์์์ ์ฐพ์ ํด๋น ์์น์ ์ปดํฌ๋ํธ ๋ ๋๋ง ๋ก์ง์ ์๋ก ์
๋ฐ์ดํธ๋ state๋ฅผ ์ฃผ๊ณ ์คํ์ํจ๋ค.
๊ฐ์ ์๋ฆฌ ๊ฐ์ ์ปดํฌ๋ํธ์ state
export default function App() {
const [isFancy, setIsFancy] = useState(false);
return (
<div>
{isFancy ? (
<Counter isFancy={true} />
) : (
<Counter isFancy={false} />
)}
<label>
<input
type="checkbox"
checked={isFancy}
onChange={e => {
setIsFancy(e.target.checked)
}}
/>
Use fancy styling
</label>
</div>
);
}
function Counter({ isFancy }) {
const [score, setScore] = useState(0);
const [hover, setHover] = useState(false);
//...
return (
<div
className={className}
onPointerEnter={() => setHover(true)}
onPointerLeave={() => setHover(false)}
>
//...
์ฌ๊ธฐ์ score state์ ๋ณด์กด ์ฌ๋ถ๋ฅผ ์๊ฐํด๋ณด์.
isFancy ๋ณํ์ ๋ฐ๋ผ Counter ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง ๋์ง๋ง state๋ ๋ณด์กด๋๋๋ฐ ์ ๊ทธ๋ด๊น? Counter ์ปดํฌ๋ํธ๋ฅผ ๋ณด๋ฉด ๋งจ ์์ div ํ๊ทธ๋ฅผ ๊ฐ์ง๋๋ฐ, ๋ฆฌ์กํธ๊ฐ ๋ณด๋ ๋ ๋ ํธ๋ฆฌ ์์๋ App์ด๋ผ๋ ํธ๋ฆฌ ๋ฃจํธ์ ์ฒซ ์์์ธ div์ด๊ธฐ ๋๋ฌธ์ด๋ค.
๊ฐ์ ์์น ๋ค๋ฅธ ์ปดํฌ๋ํธ์ state
export default function App() {
const [isFancy, setIsFancy] = useState(false);
return (
<div>
{isFancy ? (
<div>
<Counter isFancy={true} />
</div>
) : (
<section>
<Counter isFancy={false} />
</section>
)}
์ด๋ ๊ฒ div์์ section ์ผ๋ก ํ๊ทธ๊ฐ ์์ ํ ๋ณํด๋ฒ๋ฆฌ๋ฉด ๊ฐ์ ์์น๋ผ๊ณ ํ๋๋ผ๋ score state๋ ์ด๊ธฐํ๋์ด 0์ผ๋ก ๋์๊ฐ๋ค.
[!faq] ํ์ ์ด๋?
divํ๊ทธ๋ฅผ ๊ฐ์ง๋ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ผ๋ฉด? ๋ง์ฝ ๋๋ช ์score๋ผ๋ state๋ฅผ ๊ฐ์ง๊ณ ๋งจ ์๊ฐdivํ๊ทธ๋ก ์ด๋ฃจ์ด์ง ์์ ํ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ฅผ ํด๋น ์์น์Counter๋์ ๋ ๋๋ง ํ๋ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น? ๊ฐ์ ์์น์div๋๊นscorestate๊ฐ ์ ์ง๋ ๊น ์๋๋ฉด ์ด๊ธฐํ ๋ ๊น?์ด๊ธฐํ ๋๋ค. ์ปดํฌ๋ํธ ํ์ ์ด HTML ํ๊ทธ์ ์ข ๋ฅ๋ฅผ ์๋ฏธํ๋ ๊ฒ์ ์๋๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฌธ์์์ ํ ์ปดํฌ๋ํธ ์์์ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ฅผ ์ ์ํ๋ ๋ฌธ์ ์ ๋ํด ์ด์ผ๊ธฐ ํ๋ค. ๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง ๋ ๋ ๋ง๋ค ๋ถ๋ชจ ์์ ์ํ ํจ์์ธ ์์ ์ปดํฌ๋ํธ๋ ๋ค๋ฅธ ์ฃผ์๊ฐ์ ๊ฐ์ง๊ฒ ๋๋ค. ๊ทธ๋์ ๋๊ฐ์ ์์ ์ปดํฌ๋ํธ๋ก ๋ณด์ฌ๋ ์ฃผ์๊ฐ์ด ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ํ์ ์ด๋ผ๊ณ ๋ฆฌ์กํธ๋ ํ๋จํ๊ณ state๋ฅผ ์ด๊ธฐํ ํ๋ค. ๊ทธ๋ฌ๋ ์์ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ผ๋ฉด ์ ์ด์ ๋ค๋ฅธ ์ฃผ์๊ฐ์ ๊ฐ์ง๊ฒ ๋๊ณ , ๋ค๋ฅธ ํ์ ์ด ๋๋ค.
๊ฐ์ ์์น์์ state ์ด๊ธฐํ ํ๊ธฐ
์ด๋ค ์ด์ ๋ก ๊ฐ์ ์์น์์ ๊ฐ์ ์ปดํฌ๋ํธ๋ฅผ ํธ์ถํ๋ฉด์ state๊ฐ ์ด๊ธฐํ ๋๊ธฐ๋ฅผ ์ํ๋ ๊ฒฝ์ฐ๊ฐ ์์ ์ ์๋ค (๋ฌธ์์์ ๋ณด์ฌ์ฃผ๋ ์์๋ ํ์ค์ฑ์ด ๋จ์ด์ง๋๊ฒ ๊ฐ์ ๋ฐ๋ก ์ฐพ์๋ค) :
- ์ค๋ฌธ์กฐ์ฌ์ ๊ฐ์ ๋ค๋จ๊ณ ํผ - 1๋ฒ ๋ฌธํญ์ ๋ต์ ํ๊ณ ๋ค์ ๋จ๊ณ๋ก ๋์ด๊ฐ์๋ 1๋ฒ ๋ฌธํญ์ ๋ํ ๋ต์ด ๋จ์์์ ์ ์๋ค.
- ๋ธ๋ก๊ทธ ์๋ํฐ๋ ๋ฉ๋ชจ์ฑ - A๊ธ์ ์์ ํ๊ณ ์๋ค๊ฐ ์ ์ฅํ์ง ์๊ณ ๋ฆฌ์คํธ์์ ๋ฐ๋ก B๊ธ ์์ ์ผ๋ก ๋์ด๊ฐ๋ค๋ฉด A๊ธ์ ๋ด์ฉ์ด B ๊ธ์ ๋ณธ๋ฌธ ์์น์ ๊ทธ๋๋ก ์ ์ฉ๋์ด ๋ฒ๋ฆฐ๋ค.
์ด๋ด๋ ํ ์ ์๋ ํจ๊ณผ์ ์ธ ๋ฐฉ๋ฒ์ ์ปดํฌ๋ํธ์ key๊ฐ์ ์ฃผ๋๊ฒ์ด๋ค. key ๊ฐ์ ๋ฐฐ์ด์ ๋ ๋๋ง ํ ๋๋ง ํ์ํ๊ฒ์ด ์๋๋ค. ๋ฆฌ์กํธ๋ key๊ฐ ๋ณ๊ฒฝ๋๋ฉด ๋ฌด์กฐ๊ฑด ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ผ๊ณ ํ๋จํ๋ค.
์ ๊ฑฐ๋ ์ปดํฌ๋ํธ์ state ๋ณด์กดํ๊ธฐ
์ด๋ค ์ด์ ๋ก๋ ๋ฐ๋๋ก ์ฌ๋ผ์ง ์ปดํฌ๋ํธ์ state๋ฅผ ๊ธฐ์ตํ๊ณ ์ถ์ ์ ์๋ค. ์์ ์์๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํด๋ณด๋ฉด, 1๋ฒ ๋ฌธํญ์ ๋ต์ ์ ๋ค๊ฐ ์ ์ถํ์ง ์๊ณ ๋ค์ ๋ฌธํญ์ผ๋ก ๋์ด๊ฐ์ง๋ง, ๋ค์ ๋์์์๋ ์ ์๋ ๋ต์ด ๋จ์์๊ธฐ๋ฅผ ์ํ ์ ์๋ค. ์ด๋ด๋ ์ด๋ป๊ฒ ํด์ผํ ๊น?
- ์ธ๋ง์ดํธ ๋์ CSS๋ก ์๋ณด์ด๊ฒ ๋ง๋ ๋ค.
- state๋ฅผ ์์๋ก ์ฌ๋ฆฌ๊ณ ๊ฐ ๋ฌธํญ๋ณ ๋ต์ state๋ฅผ ๋ถ๋ชจ๊ฐ ๊ฐ์ง๊ณ ์๋ค๊ฐ ์์์๊ฒ props๋ก ๊ฑด๋ด์ค๋ค.
- ๋ฆฌ์กํธ ์ด์ธ์ ๋ค๋ฅธ ์ ์ฅ์๋ฅผ ์ด์ฉํ๋ค :
localStorage๋ ๋ค๋ฅธ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค.
์์ ๋ฌธ์
1. ์ ๋ ฅ๊ฐ ์ด๊ธฐํ ๋ฌธ์
export default function App() {
const [showHint, setShowHint] = useState(false);
if (showHint) {
return (
<div>
<p><i>Hint: Your favorite city?</i></p>
<Form />
<button onClick={() => {
setShowHint(false);
}}>Hide hint</button>
</div>
);
}
return (
<div>
<Form />
<button onClick={() => {
setShowHint(true);
}}>Show hint</button>
</div>
);
}
์์ ์ฝ๋์์ Form ์ปดํฌ๋ํธ์ ์์น๊ฐ ์ฒซ ๋ฒ์งธ์ ๋ ๋ฒ์งธ๋ฅผ ์ค๊ฐ๊ธฐ ๋๋ฌธ์ ๋ฆฌ๋ ๋๋ง๋ง๋ค state๊ฐ ์ด๊ธฐํ ๋๋ค.
ํด๊ฒฐํ๊ธฐ ์ํด์๋ ์ด๋ ๊ฒ ํญ์ ์ปดํฌ๋ํธ์ ์์น๋ฅผ ๊ณ ์ ์์ผ์ผ ํ๋ค.
//...
<div>
{showHint && // p ํ๊ทธ or null
<p><i>Hint: Your favorite city?</i></p>
}
<Form />
{showHint ? (
<button onClick={() => {
setShowHint(false);
}}>Hide hint</button>
) : (
<button onClick={() => {
setShowHint(true);
}}>Show hint</button>
)}
</div>
//...
2. ์ ๋ ฅ์ฐฝ ๋ง๋ฐ๊พธ๊ธฐ
export default function App() {
//...
if (reverse) {
return (
<>
<Field key="lastName" label="Last name" />
<Field key="firstName" label="First name" />
{checkbox}
</>
);
} else {
return (
<>
<Field key="firstName" label="First name" />
<Field key="lastName" label="Last name" />
{checkbox}
</>
);
};
}
function Field({ label }) {
const [text, setText] = useState('');
return (
<label>
{label}:{' '}
<input
type="text"
value={text}
placeholder={label}
onChange={e => setText(e.target.value)}
/>
</label>
);
}
์ฝ๊ฐ ํน์ํ ๊ฒฝ์ฐ์ธ๋ฐ, ๋ฆฌ์กํธ๊ฐ ๋ฆฌ๋ ๋๋ง์ ํ ๋ ๊ฐ์ key๊ฐ์ ๊ฐ์ง ์ปดํฌ๋ํธ๊ฐ ์์น๋ง ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ state๋ฅผ ๋ณด์กดํ๋ค.
5. ๋ฆฌ์คํธ์์ state
์๋ ์ฝ๋์์ ๋ฌธ์ ๋ Contact ์ปดํฌ๋ํธ์ expanded ๋ผ๋ state๊ฐ ์๋์ ๋ค๋ฅด๊ฒ ๋จ์์๊ฒ ๋๋ค๋ ์ ์ด๋ค. "Alice" ์ฐ๋ฝ์ฒ ์ปดํฌ๋ํธ์ expanded ๋ฅผ ์ฐธ์ผ๋ก ์ค์ ํ๊ณ ์์น๋ฅผ ๊ฑฐ๊พธ๋ก ๋๋ฆฌ๋ฉด ํด๋น ์์น๋ก ์ค๊ฒ๋๋ "Taylor" ์ปดํฌ๋ํธ์ expanded๊ฐ ์ฐธ์ผ๋ก ์ค์ ๋๋ค. ์ฆ key๊ฐ ๋ณ๊ฒฝ์ ๋ฐ๋ฅธ ์ํ ์ด๊ธฐํ๊ฐ ์ ์ฉ์ด ๋์ง ์๋๋ค๋ ๊ฒ.
export default function ContactList() {
const [reverse, setReverse] = useState(false);
const displayedContacts = [...contacts];
if (reverse) {
displayedContacts.reverse();
}
return (
<>
<label>
<input
type="checkbox"
checked={reverse}
onChange={e => {
setReverse(e.target.checked)
}}
/>{' '}
Show in reverse order
</label>
<ul>
{displayedContacts.map((contact, i) =>
<li key={i}>
<Contact contact={contact} />
</li>
)}
</ul>
</>
);
}
์์ธ์ key๊ฐ์ ์ธ๋ฑ์ค๋ก ์ฃผ๊ณ ์๊ธฐ ๋๋ฌธ์. ๋ฐฐ์ด์ ์ฒซ ์ธ๋ฑ์ค๋ Alice๊ฑฐ๋ Taylor๊ฑฐ๋ 0์ธ๊ฒ์ ๋ง์ฐฌ๊ฐ์ง๋ค. ๊ทธ๋์ key๊ฐ์ด ์ ์ง๋๋ props๋ก ์ ๋ฌ๋๋ contact ๊ฐ์ฒด์ ๋ํ ์ ๋ณด๋ ๋ฐ๋์ง๋ผ๋, ํด๋น ์ปดํฌ๋ํธ์ state์ธ expanded๋ ์ ์ง๊ฐ ๋๋๊ฒ. ๋ฐ๋ผ์ key๊ฐ์ ๋ฐฐ์ด ์ธ๋ฑ์ค๊ฐ ์๋๋ผ ์ง์ง ์ปดํฌ๋ํธ์ key๋ผ๊ณ ํ ์ ์๋ ๊ฐ์ผ๋ก(๊ฐ๋ น contact.id) ์ฌ๋ฐ๋ฅด๊ฒ ์ค์ ํด์ผ ํ๋ค.

