You will learn
- ์ธ์ ํ๋์ ํน์ ์ฌ๋ฌ๊ฐ์ state ๋ณ์๋ฅผ ์ฌ์ฉํด์ผ ํ๋์ง
- state๋ฅผ ๊ตฌ์ฑํ ๋ ํผํด์ผ ํ๋ ๊ฒ๋ค
- ํํ state ๊ตฌ์กฐ์ ๋ฌธ์ ๋ค์ ๋ฌด์์ด๊ณ ์ด๋ป๊ฒ ๊ณ ์น๋์ง
state ๊ตฌ์กฐํ์ ์์น
state ๊ตฌ์กฐํ์ ์์ฑ๋๋ ์์ ํ๊ณ ๋๋ฒ๊น ํ๊ธฐ ์ฆ๊ฑฐ์ด ์ปดํฌ๋ํธ์ ๊ดด๋ก์ด ์ปดํฌ๋ํธ๋ฅผ ๊ฒฐ์ ํ๋ค๊ณ ํ๋ค.
- ๊ด๋ จ์๋ state ๊ทธ๋ฃนํ - ๋ ๊ฐ ์ด์์ state ๋ณ์๊ฐ ์ธ์ ๋ ํจ๊ป ์ ๋ฐ์ดํธ ๋๋ค๋ฉด ์ด ๋ณ์๋ฅผ ๋จ์ผ state ๊ฐ์ฒด๋ก ํฉ์น๋ค.
- ๋ชจ์๋ state ํผํ๊ธฐ - ๋ ๊ฐ๊ฐ ๋์์
true์ผ ์ ์๋ state๋ค์ฒ๋ผ ์๋ก ๋ชจ์์ ์ผ์ผํค๋ state๊ฐ ์์ผ๋ฉด ์์ ํ๋ค. - ๋ถํ์ํ state ๋ง๋ค์ง ์๊ธฐ - ์ฌ๊ธฐ์ "๋ถํ์"ํ๋ค๋ ๋ง์ ๋ค๋ฅธ state๋ props์์ ๊ณ์ฐ๋ ์ ์๋ ์ ๋ณด๋ผ๋ฉด state๋ก ํด๋น ์ ๋ณด๋ฅผ ๊ด๋ฆฌ ํ ํ์๊ฐ ์๋ค๋ ๋ป์ด๋ค.
- state ์ค๋ณต ํผํ๊ธฐ - ๊ฐ์ ์ ๋ณด๊ฐ ์ฌ๋ฌ state์ ์ค๋ณต๋์ด ์กด์ฌํ ๊ฒฝ์ฐ ๋๊ธฐํํ๊ธฐ ์ด๋ ต๊ธฐ ๋๋ฌธ์ ํผํด์ผ ํ๋ค.
- ๊น๊ฒ ์ค์ฒฉ๋(nested) state ํผํ๊ธฐ - ๊น์ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง ๊ฐ์ฒด ํํ์ state๋ ์ ๋ฐ์ดํธํ๊ธฐ ์ฝ์ง ์๋ค. ๊ทธ๋์ ๊ฐ๋ฅํ ํ ํํํ(flat) ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๋๋ก ์ค๊ณํ๋ค. (ํ์ด๋ผ์ดํธ ํ์ง ์์ ์์น์ ์ด์ ๋ฌธ์์์ ๋ค๋ฃฌ ์ค๋ณต๋ ๋ด์ฉ์)
1. state ๊ทธ๋ฃนํ
์ธ์ ์ฌ๋ฌ ์ ๋ณด๋ฅผ ํ๋์ state ๋ณ์๋ก ์ฌ์ฉํด์ผํ๊ณ ์ธ์ ๋ ๊ฐ๊ฐ ๋ฐ๋ก ์ฌ๋ฌ๊ฐ์ state ๋ณ์๋ฅผ ์ฌ์ฉํด์ผ ํ ๊น? ๋ง์ฐ์ค ์ขํ์ฒ๋ผ ๊ฑฐ์ ํญ์ ๋์์ ์ ๋ฐ์ดํธ๋๋ ์ ๋ณด๋ค์ธ ๊ฒฝ์ฐ ๋ณ๋์ state๋ก ๋๋๊ฒ๋ณด๋ค ํ๋๋ก ๋ณํฉ์ํค๋๊ฒ์ด ์ข๋ค.
// X
const [x, setX] = useState(0);
const [y, setY] = useState(0);
// O
const [position, setPosition] = useState({ x: 0, y: 0 });
return (
<div
onPointerMove={e => {
setPosition({
x: e.clientX,
y: e.clientY
});
}}
//...
state ๊ฐ์ฒด์ ์ผ๋ถ ํ๋๋ง ์ ๋ฐ์ดํธ ํ ์ ์๋ค.
setPosition({x:100});
์ด๋ ๊ฒ position ์ xํ๋๋ง ์
๋ฐ์ดํธ ํ ์ ์๋ค. ํญ์ ๊ธฐ์กด position์ ๋ชจ๋ ํ๋๋ฅผ ์
๋ฐ์ดํธ ํด์ค์ผ ํ๋ค.
setPosition({...position, x:100});
์ด๋ ๊ฒ ํ๋ฉด x ํ๋๋ฅผ ์ ์ธํ ๋ชจ๋ ๊ฐ๋ค์ ๊ทธ๋๋ก ์ฌ์ฉํ๋ฉด์ x์ ๊ฐ๋ง ๋ฐ๊ฟ์ ์๋ก์ด state๋ก ์
๋ฐ์ดํธํ ์ ์๋ค.
2. ๋ชจ์๋๋ state
export default function FeedbackForm() {
const [text, setText] = useState('');
const [isSending, setIsSending] = useState(false);
const [isSent, setIsSent] = useState(false);
async function handleSubmit(e) {
e.preventDefault();
setIsSending(true);
await sendMessage(text);
setIsSending(false);
setIsSent(true);
}
//...
์์ ์ฝ๋๋ ์ ์์ ์ผ๋ก ์๋ํ์ง๋ง isSending๊ณผ isSent๋ ๋ชจ์๋๋ ์ํฉ์ ์ด๋ํ ์ ์๋ค. ์๋ฅผ๋ค์ด setIsSending(false)๋ฅผ ํ์ง๋ง setIsSent(true) ํด์ฃผ๋๊ฒ์ ๊น๋นกํ ์๋ ์๊ณ , isSending๊ณผ isSent๊ฐ ๋์์ ์ฐธ์ด๋๋ ์ญ์ค์ ๋ง๋ค์ด๋ผ ์ ์๋ค.
๊ทธ๋์ status๋ก state๋ฅผ ํตํฉํ๊ณ sending, sent, typing(์ด๊ธฐ๊ฐ) ์ค์ ํ๋์ ๊ฐ์ ๊ฐ์ง๋๋ก ํ๋ฉด ๋๋ค.
์์ ์ ์ธ
๊ฐ๋
์ฑ์ ์ํด์(isSendingํน์ isSent๊ฐ ํ์คํ ์ฝ๊ธฐ ํธํ๋ค) ์ด๋ ๊ฒ ์์๋ก ์ ์ธํ ์ ์๋ค.
const isSending = status === 'sending';
const isSent = status === 'sent';
3. ๋ถํ์ํ state
export default function Form() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [fullName, setFullName] = useState('');
function handleFirstNameChange(e) {
setFirstName(e.target.value);
setFullName(e.target.value + ' ' + lastName);
}
function handleLastNameChange(e) {
setLastName(e.target.value);
setFullName(firstName + ' ' + e.target.value);
}
์ฌ๊ธฐ์ fullName ๋ ๊ตณ์ด state ๋ณ์๋ก ๊ด๋ฆฌ ํ ํ์๊ฐ ์๋ค. firstName๊ณผ lastName์ ํตํด ๊ณ์ฐ๋ ์ ์๋ ๊ฐ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
const fullName = firstName + ' ' + lastName;
์ด๋ ๊ฒ ์ผ๋ฐ ๋ณ์๋ก ์ ์ธํ์ฌ ๊ด๋ฆฌํ๋ฉด, ๋ ๋๋ง๋ง๋ค ์ต์ ๊ฐ์ ์ฝ์ ์ ์์ผ๋ฉด์๋ ๊ตณ์ด setFullName์ผ๋ก ์
๋ฐ์ดํธ๋ฅผ ํ ํ์๋ ์์ด์ง๋ค.
4. ์ค๋ณต๋ state
export default function Menu() {
const [items, setItems] = useState(initialItems);
const [selectedItem, setSelectedItem] = useState(
items[0]
);
์์ ์ฝ๋๋ฅผ ๋ณด๋ฉด selectedItem state์ item ๊ฐ์ฒด๋ก ์ ์ฅํ๊ณ ์๋ค. ํ์ง๋ง ์ด๊ฑด ์ข์ ํจํด์ด ์๋๋ค. items์ ์ด๋ฏธ ์๋ ์ ๋ณด๋ฅผ ๊ทธ๋๋ก seletedItem๋ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์ด๋ค. ์ด ๋ ๋ฐ์ํ ์ ์๋ ๋ฌธ์ ๋ ๋ ์ ๋ณด ์ฌ์ด์ ๋๊ธฐํ๊ฐ ๋ฌธ์ ๊ฐ ๋ ์ ์๋ค.
๋ง์ฝ์ ์ด๋ ๊ฒ ๊ฐ item์ ์ด๋ฆ์ ๋ณ๊ฒฝํ ์ ์๋ค๊ณ ํด๋ณด์ :
function handleItemChange(id, e) {
setItems(items.map(item => {
if (item.id === id) {
return {
...item,
title: e.target.value,
};
} else {
return item;
}
}));
}
return(
//...
<ul>
{items.map((item, index) => (
<li key={item.id}>
<input
value={item.title}
onChange={e => {
handleItemChange(item.id, e)
}}
/>
{' '}
<button onClick={() => {
setSelectedItem(item);
}}>Choose</button>
</li>
))}
</ul>
์ฌ๊ธฐ์ ์์ดํ
์ด๋ฆ์ ๋ณ๊ฒฝํ๋ค๊ณ ํ๋ฉด items์ ์๋ ํด๋น ์์ดํ
๋ง ๋ณ๊ฒฝ๋๊ณ selectedItem์ ๋ณ๊ฒฝ๋์ง ์๋๋ค.
"pretzels" ๋ผ๋ ์์ดํ
์ ์ ํํ๋ค๋ฉดselectedItem === {id:0, name:"pretzels"} ์ด๋ ๊ฒ ๋๋ค. ๊ทธ๋ฐ๋ฐ ์ด๋ "pretzels"์ ์ด๋ฆ์ "Pretzels ํ๋ ์ฒผ"๋ก ๋ณ๊ฒฝํ๋ค๋ฉด
items = {
{
id : 0,
name : "Pretzels ํ๋ ์ฒผ"
}
//...
}
์ด๋ ๊ฒ ๋์ง๋ง selectedItem์ ๊ทธ๋๋ก {id:0, name:"pretzels"} ๋ก ๋จ์์๊ฒ ๋๋ค. (selectedItem๋ ํจ๊ป ๋ฐ๊ฟ์ฃผ๋ฉด ๋๋ ๊ฐ๋จํ ๋ฌธ์ ์ง๋ง ํญ์ ๊น๋นกํ์ง ์๋ ๊ฒ์ ์ฝ์ง ์์ ์ผ์ด๊ณ , ์ ์ด์ ๋ฌธ์ ์ ๊ฐ๋ฅ์ฑ์ ์ฐจ๋จํ๋๊ฒ ์ต์ ์ด๋ค.)
๊ทธ๋์ ์ด๋ ๊ฒ selectedId ๋ฅผ state๋ก ๊ฐ์ ธ๊ฐ๊ณ selectedItem์ ์ผ๋ฐ ๋ณ์๋ก ์ ์ธํด์ผ ํ๋ค.
const [items, setItems] = useState(initialItems);
const [selectedId, setSelectedId] = useState(0);
const selectedItem = items.find(item =>
item.id === selectedId
);
5. ๊น๊ฒ ์ค์ฒฉ๋ state
๋ค์๊ณผ ๊ฐ์ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง state๋ฅผ ๊ฐ์ ํด๋ณด์
export const initialTravelPlan = {
id: 0,
title: '(Root)',
childPlaces: [{
id: 1,
title: 'Earth',
childPlaces: [{
id: 2,
title: 'Africa',
childPlaces: [{
id: 3,
title: 'Botswana',
childPlaces: []
},
//....
]
}, {
id: 10,
title: 'Americas',
childPlaces: [{
id: 11,
title: 'Argentina',
childPlaces: []
},
//...
]
}, {
id: 19,
title: 'Asia',
childPlaces: [{
id: 20,
title: 'China',
childPlaces: []
},
//...
]
}, {
id: 26,
title: 'Europe',
childPlaces: [{
id: 27,
title: 'Croatia',
childPlaces: [],
},
//...
]
}, {
id: 34,
title: 'Oceania',
childPlaces: [{
id: 35,
title: 'Australia',
childPlaces: [],
},
//...
]
}]
}, {
id: 42,
title: 'Moon',
childPlaces: [{
id: 43,
title: 'Rheita',
childPlaces: []
}, {
id: 44,
title: 'Piccolomini',
childPlaces: []
}, {
id: 45,
title: 'Tycho',
childPlaces: []
}]
},
//...
};
์ด ๊ตฌ์กฐ์์ ์ฅ์๋ฅผ ์ถ๊ฐํ๊ฑฐ๋ ์ญ์ ํ๋ ์ฝ๋๋ ๋งค์ฐ ๋ณต์กํ๊ณ ๊ธธ์ด์ง ๊ฒ์ด๋ค. (state์
๋ฐ์ดํธ๋ ์ผ๋ถ ๊ฐ๋ง ๋ณ๊ฒฝํ ์ ์๊ณ ์ ์ฒด ๊ฐ์ ์
๋ฐ์ดํธ ํด์ผํ๋ค. ์์ - setState({...state, newState}))
ํ์ง๋ง ๋น์ฐํ๋ ์ด๋ ๊ฒ ํฐ state๊ฐ ํ์ํ ์๊ฐ ์๋ค. ์ด๋ ๊ฒ ํฐ state๋ฅผ ๋ง๋ค๋ฉด ์๋๋๊ฒ์ ์๋๋ค. ์ด๋ ์ ์ฉ ๊ฐ๋ฅํ ํ์ด "ํํํ"๋ค.
export const initialTravelPlan = {
0: {
id: 0,
title: '(Root)',
childIds: [1, 42, 46],
},
1: {
id: 1,
title: 'Earth',
childIds: [2, 10, 19, 26, 34]
},
2: {
id: 2,
title: 'Africa',
childIds: [3, 4, 5, 6 , 7, 8, 9]
},
//...
}
์ด๋ ๊ฒ ํ๋ฉด initialTravelPlan ์ ๊น์ ๊ณ์ธต์ ๊ฐ์ง๋ ๋ณต์กํ๊ฒ ๊ผฌ์ฌ์๋ ๊ฐ์ฒด๊ฐ ์๋๋ผ ํ๋์ ๊ณ์ธต์ผ๋ก ์ด๋ฃจ์ด์ง ๋จ์ํ ๋งต ํํ๊ฐ ๋๋ค. ๋ง์น ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ
์ด๋ธ ์ฒ๋ผ.
์ด์ ํํํ๊ฒ ๋ง๋ (์ ๊ทํ normalize ๋ผ๊ณ ๋ ๋ถ๋ฅธ๋ค.) state๋ฅผ ์ ๋ฐ์ดํธ ํ๋๊ฒ์ ์ฌ์์ง๋ค.
// ์ง์ฐ๋ ๋ก์ง
function handleComplete(parentId, childId) {
const parent = plan[parentId];
// Create a new version of the parent place
// that doesn't include this child ID.
const nextParent = {
...parent,
childIds: parent.childIds
.filter(id => id !== childId)
};
// Update the root state object...
setPlan({
...plan,
// ...so that it has the updated parent.
[parentId]: nextParent
});
}
์ฌ๊ท ๋ ๋๋ง
์ด๋ ๊ฒ ํธ๋ฆฌ ๊ตฌ์กฐ(๋์ ์์์์๋ ์ง์ง ํธ๋ฆฌ ๊ตฌ์กฐ๋ก ๋์ด์์๊ณ , ์ ๊ทํ ๋ ํ์๋ ๊ฐ๋ ์ ์ผ๋ก ํธ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๊ณ ์๋ค)์ปดํฌ๋ํธ๋ฅผ ์ฌ๊ท ํธ์ถํ๋ ๋ฐฉ์(DFS)์ผ๋ก ๋ ๋๋ง ํ ์ ์๋ค.
function PlaceTree({ id, placesById }) {
const place = placesById[id];
const childIds = place.childIds;
return (
<li>
{place.title}
{childIds.length > 0 && (
<ol>
{childIds.map(childId => (
<PlaceTree
key={childId}
id={childId}
placesById={placesById}
/>
))}
</ol>
)}
</li>
);
}
์์ ๋ฌธ์
1. ์ ๋ฐ์ดํธ๋์ง ์๋ ์ปดํฌ๋ํธ
export default function Clock(props) {
const [color, setColor] = useState(props.color);
return (
<h1 style={{ color: color }}>
{props.time}
</h1>
);
}
props๋ก color์ time์ ๋ฐ๊ณ ์๋๋ฐ ๋ ๋ค์ color state๋ฅผ ์ ์ธํ์ฌ ๊ฐ์ง๋ค. ์์ ์ปดํฌ๋ํธ์์ ์์ ๋ณ๊ฒฝํด๋ state์ ์ํฅ์ ์ฃผ์ง ์๊ฒ๋๋ค(์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ๋ ๋ ๊ฐ์ก๋ ์ด๊ธฐ๊ฐ์ state๋ก ๊ฐ์งํ์ง๋ง, ๊ทธ๊ฑธ ๋ณ๊ฒฝํ๋ ํธ๋ฆฌ๊ฑฐ๊ฐ ์๊ธฐ ๋๋ฌธ์). state๋ฅผ ๋ฌด๋ถ๋ณํ๊ฒ ์ฌ์ฉํ์ฌ ๋ฐ์ํ๋ ๋ฌธ์ . ์ง์ props.color๋ฅผ ๋ฐ์ ์ฌ์ฉํ๋ฉด ๋ถ๋ชจ์ state ๋ณ๊ฒฝ์ ๋ง์ถฐ ์์ด ๋ณํ๊ฒ ๋๋ค.
2. ๊ณ ์ฅ๋ ์ฒดํฌ๋ฆฌ์คํธ
๋ฌผํ์ ์ฒดํฌํ ํ์ ์ญ์ ๋ฅผ ํด๋ ์นด์ดํฐ์ ๋ฐ์๋์ง ์๋ ๋ฑ์ ๋ฌธ์ ๊ฐ ์๋ค.
let nextId = 3;
const initialItems = [
{ id: 0, title: 'Warm socks', packed: true },
{ id: 1, title: 'Travel journal', packed: false },
{ id: 2, title: 'Watercolors', packed: false },
];
export default function TravelPlan() {
const [items, setItems] = useState(initialItems);
const [total, setTotal] = useState(3);
const [packed, setPacked] = useState(1);
function handleAddItem(title) {
setTotal(total + 1);
setItems([
...items,
{
id: nextId++,
title: title,
packed: false
}
]);
}
function handleChangeItem(nextItem) {
if (nextItem.packed) {
setPacked(packed + 1);
} else {
setPacked(packed - 1);
}
setItems(items.map(item => {
if (item.id === nextItem.id) {
return nextItem;
} else {
return item;
}
}));
}
function handleDeleteItem(itemId) {
setTotal(total - 1);
setItems(
items.filter(item => item.id !== itemId)
);
}
return (
<>
<AddItem
onAddItem={handleAddItem}
/>
<PackingList
items={items}
onChangeItem={handleChangeItem}
onDeleteItem={handleDeleteItem}
/>
<hr />
<b>{packed} out of {total} packed!</b>
</>
);
}
total, packed๊ฐ์ ๋ถํ์ํ state๋ค์ ๊ฐ์ง๊ธฐ ๋๋ฌธ์ ๋ฐ์ํ๋ ๋ฌธ์ . ์ ํํ๋ ์ด state๋ค์ ์ ๋๋ก ๋ณ๊ฒฝํ์ง ์๊ธฐ ๋๋ฌธ์ ๋ฐ์ํ๋ ๋ฌธ์ ์ด์ง๋ง.
handleDeleteItem์์packedstate๋ฅผ ์ ๋ฐ์ดํธ ํ์ง ์๋๋ค.
3. ์ ํ ์ฌ๋ผ์ง
์ฝ๋๊ฐ '์ ์์ '์ผ๋ก ์๋์ ํ๊ธฐ๋ ํ๋ค. ๋ค๋ง "star"์ "unstar"๋ฅผ ๋๋ฌ ๋ ์ฌ์ด๋ฅผ ์ค๊ฐ ๋ ์ ๊น ํธ๋ฒํ ๋ ๋ณด์ด๋ ํ์ด๋ผ์ดํธ ํจ๊ณผ๊ฐ ์ ๊น ๊บผ์ง๋ค. ์ ํํ๋ ๋ง์ฐ์ค๋ฅผ ์์ง์ด์ง ์์ผ๋ฉด ํ์ด๋ผ์ดํธ๊ฐ ๋์์ค์ง ์๋๋ค.
export default function MailClient() {
const [letters, setLetters] = useState(initialLetters);
const [highlightedLetter, setHighlightedLetter] = useState(null);
function handleHover(letter) {
setHighlightedLetter(letter);
}
function handleStar(starred) {
setLetters(letters.map(letter => {
if (letter.id === starred.id) {
return {
...letter,
isStarred: !letter.isStarred
};
} else {
return letter;
}
}));
}
return (
<>
<h2>Inbox</h2>
<ul>
{letters.map(letter => (
<Letter
key={letter.id}
letter={letter}
isHighlighted={
letter === highlightedLetter
}
onHover={handleHover}
onToggleStar={handleStar}
/>
))}
</ul>
</>
);
}
๋ฐ์
๋ณด๋ ์ฒซ๋์ ๋ฌธ์ ์ ์์ธ์ด ๋ฌด์์ธ์ง๋ ์ผ๋จ ์๊ฒ ๋ค. highlightedLetter๊ฐ letter์ ์ค๋ณต๋ state์ด๊ธฐ ๋๋ฌธ์ธ ๊ฒ ๊ฐ๋ค. ์ฆ ๊ฐ์ฒด ์ ์ฒด๋ฅผ state๋ก ์ฌ์ฉํ๋ฉด ์๋๊ณ ์ ํ๋ letter์ id๋ง state๋ก ์ฌ์ฉํด์ผ ํ๋ ๊ฒ ๊ฐ๋ค. ๊ทธ๋ฐ๋ฐ ๊ทธ๊ฒ ์ ์ด๋ฐ ๋ฌธ์ ๋ฅผ ์ผ์ผํค๋๊ฑฐ์ง..? ์ด์ ๋ฅผ ์๊ธฐ ์ํด ๋ค์ ์๋ก ์ฌ๋ผ๊ฐ์ ๋ณธ๋ฌธ์ ์ฝ์๋ค.
๊ทธ๋ฌ๋๊น "star" ํน์ "unstar" ๋ฒํผ์ ๋๋ฅด๋ฉด letters state๊ฐ ์
๋ฐ์ดํธ ๋๋ค. ํ์ง๋ง highlightedLetter๋ ์์ง ์
๋ฐ์ดํธ ์ ์ ๋จธ๋ฌผ๋ฌ ์๊ณ , ๋น๋ก์ ์ฌ์ฉ์๊ฐ ๋ง์ฐ์ค๋ฅผ ์์ง์ฌ์ผ setHilightedLetter๊ฐ ํธ์ถ๋์ด ์
๋ฐ์ดํธ ๋๋ ๋๊ธฐํ๊ฐ ์ด๋ฃจ์ด์ง๊ธฐ ๋๋ฌธ์.

