react - Learn React(Managing State)
๐ React
์ด ๊ธ์ ๋ฆฌ์กํธ ๊ณต์๋ฌธ์ - ํจ์ ์ปดํฌ๋ํธ๋ฅผ ์ฝ๊ณ ์์ฑํ ๊ธ์ ๋๋ค. ๋ชจ๋ ๋ด์ฉ์ ๋ค๋ฃจ์ง๋ ์๊ณ ๊ฐ์ธ์ ์ผ๋ก ๋ถ์กฑํ๋ค๊ณ ๋๊ผ๋ ๋ถ๋ถ, ์๋กญ๊ฒ ์๊ฒ ๋ ๋ถ๋ถ๋ค์ ๊ฐ๋จํ๊ฒ ์ ๋ฆฌํ ์์ ์ ๋๋ค.
Learn React - Managing State
Reacting-to-input-with-state
- React๋ ์ ์ธํ UI ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- ํ์๊ธฐ์ฌ์๊ฒ ๋ชฉ์ ์ง๋ง ๋งํ๋ฉด ์์์ ๊ฐ๋ ๊ฒ์ฒ๋ผ, React๋ ์ํ์ ๋ฐ๋ผ UI๋ฅผ ์๋์ผ๋ก ์ ๋ฐ์ดํธ
Thinking about UI declaratively
- ์ปดํฌ๋ํธ์ ๋ค์ํ ์๊ฐ์ ์ํ๋ฅผ ์๋ณํ๋ค.
- ์ํ ๋ณํ๋ฅผ ์ด๋ฐํ๋ ์์๋ฅผ ํ์ ํ๋ค.
- useState๋ฅผ ์ฌ์ฉํ์ฌ ๋ฉ๋ชจ๋ฆฌ์ ์ํ๋ฅผ ํํํ๋ค.
- ๋นํ์์ ์ธ state ๋ณ์๋ฅผ ์ ๊ฑฐํ๋ค.
- ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ์ฐ๊ฒฐํ์ฌ state๋ฅผ ์ค์ ํ๋ค.
Step 1: Identify your componentโs different visual states
Displaying many visual states at once
์ปดํฌ๋ํธ์ ์๊ฐ์ ์ํ๊ฐ ๋ง์ ๊ฒฝ์ฐ ํ ํ์ด์ง์ ๋ชจ๋ ํ์ํด๋ณด๋ ๊ฒ์ด ํธ๋ฆฌํ ์ ์๋ค.(๊ฐ์ธ์ ์ผ๋ก ๊ฐ๋จํ๊ฒ ์์ผ๋ก๋ง ์์ ํด๋๋ ๊ฒ ์ข๋ค๊ณ ์๊ฐํ๋ค.) ์ด๋ฐ ํ์ด์ง๋ฅผ โliving style guideโ ํน์ โstorybookโ์ด๋ผ๊ณ ํ๋ค.
import Form from './Form.js';
let statuses = [
'empty',
'typing',
'submitting',
'success',
'error',
];
export default function App() {
return (
<>
{statuses.map(status => (
<section key={status}>
<h4>Form ({status}):</h4>
<Form status={status} />
</section>
))}
</>
);
}
Step 2: Determine what triggers those state changes
์ํ ๋ณ๊ฒฝ์ ์ผ์ผํค๋ ์์๋ฅผ ํ์ ํด์ผํ๋ค. ํฌ๊ฒ 2๊ฐ์ง๋ก ๋๋ ์ ์๋ค.
- ์ฌ๋์ ์ ๋ ฅ : ๋ฒํผ ํด๋ฆญ, ํ๋ ์ ๋ ฅ, ๋งํฌ ์ด๋ ๋ฑ
- ์ปดํจํฐ์ ์ ๋ ฅ : ๋คํธ์ํฌ์์ ์๋ต ๋์ฐฉ, ์๊ฐ ์ด๊ณผ, ์ด๋ฏธ์ง ๋ก๋ฉ ๋ฑ
์ฌ๋์ ์ ๋ ฅ์๋ ์ข ์ข ์ด๋ฒคํธ ํธ๋ค๋ฌ๊ฐ ํ์ํ๋ค!(
handleXXX
)
Step 3: Represent the state in memory with useState
์ด์ ๊ฐ ์ปดํฌ๋ํธ์์์ ๋ฉ๋ชจ๋ฆฌ ์ญํ ์ ํ๋ state
๋ฅผ useState๋ก ์ ์ํด์ผํ๋ค. ์ด ๋, ๊ฐ์ฅ ์ต์ํ์ state๋ฅผ ์ ์ํ๋ ๊ฒ์ด ์ข๋ค.
๋ฐ๋์ ํ์ํ state๋ถํฐ ์ ์ํ๋๋ก ํ์! ์ฆ์ state๊ฐ ์ ๋ฆฌ๋์ง ์๋๋ค๋ฉด, ์ผ๋จ ๊ฐ๋ฅํ state๋ฅผ ๋ค ์ ์ด๋ณด๊ณ ํ๋์ฉ ์ณ๋ด๋ฉด ๋๋ค.
Step 4: Remove any non-essential state variables
์ด ๋จ๊ณ์์์ ๋ชฉํ๋ state๊ฐ ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ฃผ๊ธฐ๋ฅผ ์ํ๋ UI๋ฅผ ๋ณด์ฌ์ฃผ์ง ์๋ ๊ฒฝ์ฐ๋ฅผ ์ ๊ฑฐํ๋ ๊ฒ์ด๋ค. ์๋์ 3๊ฐ์ง๋ฅผ ๊ณ ๋ คํด๋ณด์.
- state๊ฐ ๋ชจ์์ ์ผ๊ธฐํ๋์ง?(ex.
isSubmitting
๊ณผisSuccess
๊ฐ ๋์์ true์ธ ๊ฒฝ์ฐ) - ๋ค๋ฅธ state์ ์ด๋ฏธ ๊ฐ์ ์ ๋ณด๊ฐ ์๋์ง?
- ๋ค๋ฅธ state๋ฅผ ๋ค์ง์ผ๋ฉด ๊ฐ์ ์ ๋ณด๋ฅผ ์ป์ ์ ์๋์ง?
Eliminating โimpossibleโ states with a reducer
useReducer
๋ฅผ ์ฌ์ฉํ๋ฉด state๋ฅผ ๋ ์ ๊ด๋ฆฌํ ์ ์๋ค.- ์กฐ๊ธ ๋ ์ ํํ๊ฒ state๋ฅผ ๋ชจ๋ธ๋งํ๊ธฐ ์ํด
useReducer
๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
Step 5: Connect the event handlers to set state
์ด์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ์ฐ๊ฒฐํ์ฌ state๋ฅผ ์ค์ ํด๋ณด์. ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ handleXXX
์ ๊ฐ์ ์ด๋ฆ์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
Choosing the State Structure
Principles for structuring state
state๋ฅผ ๊ตฌ์กฐํํ๋ ๋ฐฉ๋ฒ์๋ ์ฌ๋ฌ ๊ฐ์ง๊ฐ ์๋ค. ์ด ๋, state๋ฅผ ๊ตฌ์กฐํํ๋ ๋ฐ ์์ด์ ๋ช ๊ฐ์ง ์์น์ ์งํค๋ ๊ฒ์ด ์ข๋ค.
- ๊ด๋ จ state๋ฅผ ๊ทธ๋ฃนํํด๋ผ. ํญ์ ๋ ๊ฐ ์ด์์ state ๋ณ์๋ฅผ ๋์์ ์ ๋ฐ์ดํธํ๋ ๊ฒฝ์ฐ ๋จ์ผ state ๋ณ์๋ก ๋ณํฉํ๋ ๊ฒ์ด ์ข๋ค.
- state์ ๋ชจ์์ ํผํด๋ผ.
- ๋ถํ์ํ state๋ฅผ ํผํด๋ผ.
- state ์ค๋ณต์ ํผํด๋ผ.
- ๊น๊ฒ ์ค์ฒฉ๋ state๋ ํผํด๋ผ.
์์ ๊ณผ์ ์ DB ์์ง๋์ด๊ฐ ๋ฒ๊ทธ๋ฅผ ์ค์ด๊ธฐ ์ํด DB๋ฅผ ์ ๊ทํํ๋ ๊ฒ๊ณผ ์ ์ฌํ ์์ ์ด๋ค.
์๋์ ๊ฐ์ด
handleXXX
์ ํจ์์์ event ์ธ์๋ ํญ์ ๋ง์ง๋ง์ ์์นํด์ผํ๋ค.
function handleItemChange(id, e) {
setItems(items.map(item => {
if (item.id === id) {
return {
...item,
title: e.target.value,
};
} else {
return item;
}
}));
}
Sharing State Between Components
Lifting state up
๋ ๊ฐ์ ์ปดํฌ๋ํธ๊ฐ ๋์ผํ state๋ฅผ ๊ณต์ ํด์ผํ๋ ๊ฒฝ์ฐ, state๋ฅผ ๋ ์ปดํฌ๋ํธ์ ๊ณตํต ๋ถ๋ชจ ์ปดํฌ๋ํธ๋ก ์ฎ๊ธฐ๋ ๊ฒ์ด ์ข๋ค. ๊ทธ ์ ์ฐจ๋ ์๋์ ๊ฐ๋ค.
- ์์ ์ปดํฌ๋ํธ์์ state๋ฅผ ์ ๊ฑฐํ๋ค.
- ๊ณตํต ๋ถ๋ชจ ์ปดํฌ๋ํธ์ ํ๋ ์ฝ๋ฉ๋ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ค.
- ๊ณตํต ๋ถ๋ชจ ์ปดํฌ๋ํธ์ state๋ฅผ ์ถ๊ฐํ๊ณ ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ํจ๊ป ์์ ์ปดํฌ๋ํธ์ ์ ๋ฌํ๋ค.
Controlled and uncontrolled components
์ผ๋ฐ์ ์ผ๋ก ์ผ๋ถ ๋ก์ปฌ state๋ฅผ ๊ฐ์ง ์ปดํฌ๋ํธ๋ฅผ โuncontrolled componentโ๋ผ๊ณ ํ๋ค. ๋ฐ๋ฉด, ๋ชจ๋ state๋ฅผ ๋ถ๋ชจ ์ปดํฌ๋ํธ๋ก ์ฎ๊ธด ์ปดํฌ๋ํธ๋ฅผ โcontrolled componentโ๋ผ๊ณ ํ๋ค.
๋ํ์ ์ผ๋ก <input />
์ value๊ฐ state์ ์ํด ๊ฒฐ์ ๋๋ ๊ฒฝ์ฐ controlled component๋ผ๊ณ ํ ์ ์๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด uncontrolled component๋ผ๊ณ ํ ์ ์๋ค.
A single source of truth for each state
state๋ฅผ ๊ณต์ ํ๋ ์ปดํฌ๋ํธ๊ฐ ๋ง์์ง์๋ก state๋ฅผ ๊ด๋ฆฌํ๊ธฐ๊ฐ ์ด๋ ค์์ง๋ค. ์ด ๋, ๊ฐ ๊ณ ์ ํ state๋ค์ ๋ํด ํด๋น state๋ฅผ โ์์ โํ๋ ์ปดํฌ๋ํธ๋ฅผ ์ ํํ๊ฒ ๋๋๋ฐ, ์ด ์ปดํฌ๋ํธ๋ฅผ โsingle source of truthโ๋ผ๊ณ ํ๋ค. ์ด ๋, state๋ฅผ ๊ด๋ฆฌํ๋ ์ปดํฌ๋ํธ๋ state๋ฅผ ๋ณ๊ฒฝํ๋ ํจ์๋ฅผ ์์ ์ปดํฌ๋ํธ์ ์ ๋ฌํด์ผํ๋ค. ์ด ๋, ์์ ์ปดํฌ๋ํธ๋ state๋ฅผ ๋ณ๊ฒฝํ๋ ํจ์๋ฅผ ํธ์ถํ์ฌ state๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ค.
๋จ์ํ๊ฒ ๋งํ๋ฉด ๊ฐ state์ ๋ํ ๊ณต๊ธ์ ์ญํ ์ ํ๋ ์ปดํฌ๋ํธ๋ฅผ ๊ฒฐ์ ํด์ผํ๋ค๋ ์๋ฏธ์ด๋ค. ์ด ๋, state ๋์ด์ฌ๋ฆฌ๊ธฐ ๊ธฐ๋ฒ์ด ์ฌ์ฉ๋๋ ๊ฒ์ด๋ค.
Preserving and Resetting State
state๋ ์ปดํฌ๋ํธ ๊ฐ์ ๊ฒฉ๋ฆฌ๋๋ค. React๋ UI ํธ๋ฆฌ์์ ์ด๋ค ์ปดํฌ๋ํธ๊ฐ ์ด๋ค state์ ์ํ๋์ง๋ฅผ ์ถ์ ํ๋ค. state๋ฅผ ์ธ์ ๋ณด์กดํ๊ณ ์ธ์ ์ด๊ธฐํํ ์ง๋ฅผ ์ ์ดํ ์ ์๋ค.
The UI tree
๋ธ๋ผ์ฐ์ ๋ UI๋ฅผ ๋ชจ๋ธ๋งํ๊ธฐ ์ํด ์๋ง์ ํธ๋ฆฌ
๋ฅผ ์ฌ์ฉํ๋ค. DOM์ HTML ์์๋ฅผ ๋ํ๋ด๊ณ , CSSOM์ CSS์ ๋ํด ๋์ผํ ์ญํ ์ ํ๋ค. ์ฌ์ง์ด ์ ๊ทผ์ฑ ํธ๋ฆฌ๋ ์กด์ฌํ๋ค!
๋ง์ฐฌ๊ฐ์ง๋ก ๋ฆฌ์กํธ๋ ํธ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ๋ฐ์๊ฐ ์์ฑํ UI๋ฅผ ๊ด๋ฆฌํ๊ณ ๋ชจ๋ธ๋งํ๋ค. ๋ฆฌ์กํธ๋ JSX๋ก๋ถํฐ UI ํธ๋ฆฌ๋ฅผ ๊ตฌ์ฑํ๋ค. ๊ทธ ๋ค์ ๋ฆฌ์กํธ DOM์ด ํด๋น UI ํธ๋ฆฌ์ ์ผ์นํ๋๋ก ๋ธ๋ผ์ฐ์ DOM ์๋ฆฌ๋จผํธ๋ค์ ์ ๋ฐ์ดํธํ๋ค.(React Native๋ ํธ๋ฆฌ๋ฅผ ๋ชจ๋ฐ์ผ ํ๋ซํผ์ ๋ง๋ ์๋ฆฌ๋จผํธ๋ก ๋ณํํ๋ค.)
State is tied to a position in the tree
ํํ state๊ฐ ํด๋น ์ปดํฌ๋ํธ ๋ด๋ถ
์ ์์นํ๋ค๊ณ ์๊ฐํ ์ ์๋ค. ๊ทธ๋ฌ๋ state๋ ๋ฆฌ์กํธ ๋ด๋ถ
์ ์์นํ๋ค. ์ฆ, state๋ ๋ฆฌ์กํธ๊ฐ ํด๋น ์ปดํฌ๋ํธ์ ์์น
์ ๋ฐ๋ผ ๊ฒฐ์ ํ๋ ๊ฒ์ด๋ค.
์๋์ ๊ฐ์ ์์ ์ฝ๋์์ ๊ฐ๊ฐ์ Counter ์ปดํฌ๋ํธ์ score๋ฅผ ์ฆ๊ฐ์ํจ ํ, 2๋ฒ์งธ ์ปดํฌ๋ํธ๋ฅผ ์ ๊ฑฐํ๋ค๊ฐ ๋ค์ ์์ฑํ๋ฉด 2๋ฒ์งธ ์ปดํฌ๋ํธ์ score๊ฐ ์ด๊ธฐํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๋ฆฌ์กํธ๋ ์ปดํฌ๋ํธ๊ฐ UI ํธ๋ฆฌ์ ํด๋น ์์น์์ ๋ ๋๋ง๋๋ ๋์ ์ปดํฌ๋ํธ์ state๋ฅผ ์ ์งํ๋ค. ๊ทธ๋ฌ๋ ์ปดํฌ๋ํธ๊ฐ ์ ๊ฑฐ๋๊ฑฐ๋ ๊ฐ์ ์์น์ ๋ค๋ฅธ ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง๋๋ฉด ๋ฆฌ์กํธ๋ ํด๋น ์ปดํฌ๋ํธ์ state๋ฅผ ์ญ์ ํ๋ค.
import { useState } from 'react';
export default function App() {
const [showB, setShowB] = useState(true);
return (
<div>
<Counter />
{showB && <Counter />}
<label>
<input
type="checkbox"
checked={showB}
onChange={e => {
setShowB(e.target.checked)
}}
/>
Render the second counter
</label>
</div>
);
}
function Counter() {
const [score, setScore] = useState(0);
const [hover, setHover] = useState(false);
let className = 'counter';
if (hover) {
className += ' hover';
}
return (
<div
className={className}
onPointerEnter={() => setHover(true)}
onPointerLeave={() => setHover(false)}
>
<h1>{score}</h1>
<button onClick={() => setScore(score + 1)}>
Add one
</button>
</div>
);
}
Same component at the same position preserves state
์์ ์์ ์ ๋ค๋ฅด๊ฒ ๋์ผํ ์์น์ ์๋ ๋์ผํ ์ปดํฌ๋ํธ๋ state๋ฅผ ๋ณด์กดํ๋ค.
import { useState } from 'react';
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);
let className = 'counter';
if (hover) {
className += ' hover';
}
if (isFancy) {
className += ' fancy';
}
return (
<div
className={className}
onPointerEnter={() => setHover(true)}
onPointerLeave={() => setHover(false)}
>
<h1>{score}</h1>
<button onClick={() => setScore(score + 1)}>
Add one
</button>
</div>
);
}
์ฒดํฌ๋ฐ์ค๋ฅผ ์ ํํ๊ฑฐ๋ ์ ํ ์ทจ์ํด๋ ์นด์ดํฐ state๋ ์ฌ์ค์ ๋์ง ์๋๋ค.
isFancy๊ฐ true์ด๋ false์ด๋ , ๋ฃจํธ App ์ปดํฌ๋ํธ์์ ๋ฐํ๋ div์ ์ฒซ ๋ฒ์งธ ์์์๋ ํญ์
๋ฆฌ์กํธ์์
์ปดํฌ๋ํธ์ ์์น
๊ฐ ์๋ฏธํ๋ ๊ฒ์์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง๋๋ ์์น
์ด๋ค. ์ฆ, ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง๋๋ ์์น๊ฐ ๋์ผํ๋ค๋ฉด state๋ฅผ ๋ณด์กดํ๋ค.(JSX ๋งํฌ์ ์ด ์ค์ํ ๊ฒ์ด ์๋๋ค!)
import { useState } from 'react';
export default function App() {
const [isFancy, setIsFancy] = useState(false);
if (isFancy) {
return (
<div>
<Counter isFancy={true} />
<label>
<input
type="checkbox"
checked={isFancy}
onChange={e => {
setIsFancy(e.target.checked)
}}
/>
Use fancy styling
</label>
</div>
);
}
return (
<div>
<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);
let className = 'counter';
if (hover) {
className += ' hover';
}
if (isFancy) {
className += ' fancy';
}
return (
<div
className={className}
onPointerEnter={() => setHover(true)}
onPointerLeave={() => setHover(false)}
>
<h1>{score}</h1>
<button onClick={() => setScore(score + 1)}>
Add one
</button>
</div>
);
}
์ ์์ ์์ checkbox๋ฅผ ์ ํํ๋ฉด state๊ฐ ์ฌ์ค์ ๋ ๊ฒ์ผ๋ก ์์ํ ์ ์์ง๋ง ๊ทธ๋ ์ง ์๋ค.
์ด ๋
Different components at the same position reset state
๋ฐ๋ฉด ๋์ผํ ์์น๋๋ผ๋ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ๋ฉด state๊ฐ ์ฌ์ค์ ๋๋ค.(๋๋ฌด ๋น์ฐํ ์ด์ผ๊ธฐ ๊ฐ๊ธฐ๋..?)
import { useState } from 'react';
export default function App() {
const [isFancy, setIsFancy] = useState(false);
return (
<div>
{isFancy ? (
<div>
<Counter isFancy={true} />
</div>
) : (
<section>
<Counter isFancy={false} />
</section>
)}
<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);
let className = 'counter';
if (hover) {
className += ' hover';
}
if (isFancy) {
className += ' fancy';
}
return (
<div
className={className}
onPointerEnter={() => setHover(true)}
onPointerLeave={() => setHover(false)}
>
<h1>{score}</h1>
<button onClick={() => setScore(score + 1)}>
Add one
</button>
</div>
);
}
์์ ์ฝ๋์์๋
์์ ์ด์ ๋ก ํจ์ ์ปดํฌ๋ํธ์ ์ ์๋ฅผ ์ค์ฒฉํด์๋ ์๋๋ค.
import { useState } from 'react';
export default function MyComponent() {
const [counter, setCounter] = useState(0);
function MyTextField() {
const [text, setText] = useState('');
return (
<input
value={text}
onChange={e => setText(e.target.value)}
/>
);
}
return (
<>
<MyTextField />
<button onClick={() => {
setCounter(counter + 1)
}}>Clicked {counter} times</button>
</>
);
}
MyTextField๊ฐ MyComponent ๋ด๋ถ์ ์ ์๋์ด์๊ธฐ ๋๋ฌธ์ MyComponent๊ฐ ๋ ๋๋ง๋ ๋๋ง๋ค MyTextField๊ฐ ์ฌ์ ์๋๋ค. ๊ทธ๋ฌ๋ฉด state๊ฐ ๊ณ์ ์ฌ์ค์ ๋๋ค.
Resetting state at the same position
๋์ผํ ์์น์์ state๋ฅผ ์ฌ์ค์ ํ๋ ๋ฐฉ๋ฒ์ ํฌ๊ฒ 2๊ฐ์ง๊ฐ ์๋ค.
๋จผ์ ์๋ ์์ ๋ Counter ์ปดํฌ๋ํธ๊ฐ ๋์ผํ ์์น์ ์์ผ๋ฏ๋ก state๊ฐ ๋ณด์กด๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
import { useState } from 'react';
export default function Scoreboard() {
const [isPlayerA, setIsPlayerA] = useState(true);
return (
<div>
{isPlayerA ? (
<Counter person="Taylor" />
) : (
<Counter person="Sarah" />
)}
<button onClick={() => {
setIsPlayerA(!isPlayerA);
}}>
Next player!
</button>
</div>
);
}
function Counter({ person }) {
const [score, setScore] = useState(0);
const [hover, setHover] = useState(false);
let className = 'counter';
if (hover) {
className += ' hover';
}
return (
<div
className={className}
onPointerEnter={() => setHover(true)}
onPointerLeave={() => setHover(false)}
>
<h1>{person}'s score: {score}</h1>
<button onClick={() => setScore(score + 1)}>
Add one
</button>
</div>
);
}
๊ทธ๋ ๋ค๋ฉด state๋ฅผ resetํ๋ ค๋ฉด ์ด๋ป๊ฒ ํ ์ ์์๊น?
Option 1: Rendering a component in different positions
import { useState } from 'react';
export default function Scoreboard() {
const [isPlayerA, setIsPlayerA] = useState(true);
return (
<div>
{isPlayerA &&
<Counter person="Taylor" />
}
{!isPlayerA &&
<Counter person="Sarah" />
}
<button onClick={() => {
setIsPlayerA(!isPlayerA);
}}>
Next player!
</button>
</div>
);
}
function Counter({ person }) {
const [score, setScore] = useState(0);
const [hover, setHover] = useState(false);
let className = 'counter';
if (hover) {
className += ' hover';
}
return (
<div
className={className}
onPointerEnter={() => setHover(true)}
onPointerLeave={() => setHover(false)}
>
<h1>{person}'s score: {score}</h1>
<button onClick={() => setScore(score + 1)}>
Add one
</button>
</div>
);
}
์์ ๋น์ทํ ์์ ๊ฐ์ง๋ง, Counter ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ๋ ๋ฐฉ๋ฒ์ด ๋ค๋ฅด๋ค. ์ด๋ ๊ฒ ํ๋ฉด Counter ์ปดํฌ๋ํธ๊ฐ ๋์ผํ ์์น์ ๋ ๋๋ง๋์ง ์์ผ๋ฏ๋ก state๊ฐ ์ฌ์ค์ ๋๋ค. ์์ ์ ๊ฐ์ด ์์น๊ฐ 2๊ฐ์ผ ๋๋ ๊ด์ฐฎ์ง๋ง, 3๊ฐ ์ด์์ผ ๋๋ ์ฝ๋๊ฐ ๋ณต์กํด์ง๋ค.
Option 2: Resetting state with a key
๋ชฉ๋ก์ ๋ ๋๋งํ ๋ key๋ฅผ ์ฌ์ฉํ๋ค. key๋ ๋ชฉ๋ก์๋ง ์ฌ์ฉ๋๋ ๊ฒ์ด ์๋๋ค. key๋ฅผ ์ฌ์ฉํด React๊ฐ ๋ชจ๋ ์ปดํฌ๋ํธ๋ฅผ ๊ตฌ๋ถํ๋๋ก ํ ์ ์๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก React๋ ๋ถ๋ชจ ๋ด์ ์์(โ์ฒซ ๋ฒ์งธ counterโ, โ๋ ๋ฒ์งธ counterโ)๋ฅผ ์ฌ์ฉํด ์ปดํฌ๋ํธ๋ฅผ ๊ตฌ๋ถํ๋ค. ํ์ง๋ง key๋ฅผ ์ฌ์ฉํ๋ฉด ์ด๊ฒ์ด ์ฒซ ๋ฒ์งธ counter๋ ๋ ๋ฒ์งธ counter๊ฐ ์๋๋ผ ํน์ counter(์: Taylor์ counter)์์ React์ ์๋ฆด ์ ์๋ค. ์ฆ, ์์์ ๋ํด ์ข๋ ๋ช ์์ ์ผ๋ก ์ ๋ฌํ ์ ์๋ ๊ฒ์ด๋ค.
import { useState } from 'react';
export default function Scoreboard() {
const [isPlayerA, setIsPlayerA] = useState(true);
return (
<div>
{isPlayerA ? (
<Counter key="Taylor" person="Taylor" />
) : (
<Counter key="Sarah" person="Sarah" />
)}
<button onClick={() => {
setIsPlayerA(!isPlayerA);
}}>
Next player!
</button>
</div>
);
}
function Counter({ person }) {
const [score, setScore] = useState(0);
const [hover, setHover] = useState(false);
let className = 'counter';
if (hover) {
className += ' hover';
}
return (
<div
className={className}
onPointerEnter={() => setHover(true)}
onPointerLeave={() => setHover(false)}
>
<h1>{person}'s score: {score}</h1>
<button onClick={() => setScore(score + 1)}>
Add one
</button>
</div>
);
}
Counter ์ปดํฌ๋ํธ๊ฐ ๋์ผํ ์์น์ ์กด์ฌํ์ง๋ง key ๊ฐ์ ๋ค๋ฅด๊ฒ ์ค์ผ๋ก์จ ๋์ผํ ์์น์ ์๋๋ผ๋ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ก ์ธ์ํ๊ฒ ํ ์ ์๋ ๊ฒ์ด๋ค.
key๋ ์ ์ญ์ผ๋ก ๊ณต์ ํ๋ ๊ฒ์ด ์๋, ํด๋น ๋ถ๋ชจ ์ปดํฌ๋ํธ์์๋ง ๊ณต์ ํ๋ค.
Resetting a form with a key
import { useState } from 'react';
import Chat from './Chat.js';
import ContactList from './ContactList.js';
export default function Messenger() {
const [to, setTo] = useState(contacts[0]);
return (
<div>
<ContactList
contacts={contacts}
selectedContact={to}
onSelect={contact => setTo(contact)}
/>
<Chat key={to.id} contact={to} />
</div>
)
}
const contacts = [
{ id: 0, name: 'Taylor', email: 'taylor@mail.com' },
{ id: 1, name: 'Alice', email: 'alice@mail.com' },
{ id: 2, name: 'Bob', email: 'bob@mail.com' }
];
input์ ๋ด๊ณ ์๋ Chat ์ปดํฌ๋ํธ์ key๋ฅผ ์ ๋ฌํจ์ผ๋ก์จ input์ state๋ฅผ resetํ ์ ์๋ค.
Preserving state for removed components
๋ง์ฝ ๊ฐ key์ ํด๋นํ๋ ์ปดํฌ๋ํธ๋ง๋ค ์ด์ ๊ฐ์ ๊ธฐ์ตํ๊ฒ ํ๊ณ ์ถ๋ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น?
- ๋ชจ๋ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ๋ ๋ค๋ฅธ ๋ชจ๋ ์ปดํฌ๋ํธ๋ฅผ CSS๋ก ์จ๊ธด๋ค. ์ปดํฌ๋ํธ์ ๊ฐฏ์๊ฐ ๋ง์ผ๋ฉด ์ฑ๋ฅ์ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์๋ค. ๊ทธ๋ฌ๋ฏ๋ก ๊ฐ๋จํ ์ปดํฌ๋ํธ๋ฅผ ์์ ํ ๋ ์ ์ฉํ๋ค.
- ๋ถ๋ชจ ์ปดํฌ๋ํธ์ state๋ฅผ ๋์ด์ฌ๋ ค์ ๋ณด๊ดํ๋ ๋ฐฉ๋ฒ์ด ์๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์์ ์ปดํฌ๋ํธ๊ฐ ์ ๊ฑฐ๋๋๋ผ๋ ์ค์ํ ์ ๋ณด๋ฅผ ๋ณด๊ดํ๋ ๊ฒ์ ๋ถ๋ชจ ์ปดํฌ๋ํธ์ด๋ฏ๋ก ๋ฌธ์ ๊ฐ ๋์ง ์๋๋ค.(์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ)
- React state ์ธ์ ๋ค๋ฅธ ์์ค๋ฅผ ์ฌ์ฉํ ์๋ ์๋ค. ์๋ฅผ ๋ค์ด, ์ฌ์ฉ์๊ฐ ์ค์๋ก ํ์ด์ง๋ฅผ ๋ซ์๋ ๋ฉ์์ง ์ด์์ด ์ ์ง๋๊ธฐ๋ฅผ ์ํ ์ ์๋ค. ์ด๋ฅผ ๊ตฌํํ๊ธฐ ์ํด localStorage๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
์ด๋ค ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ ๊ฐ๋ ์ ์ผ๋ก ๊ตฌ๋ถ๋์ด์ผ ํ๋ ์ปดํฌ๋ํธ๋ผ๋ฉด key๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.