1. 예제로 알아보는 state 끌어올리기
2. 각 state의 단일 진실 공급원(SSOT)
이 예제에서는 부모 컴포넌트인 Accordion
컴포넌트가 두 개의 Panel 컴포넌트를 렌더링합니다.
Accordion
Panel
Panel
각 Panel
컴포넌트는 콘텐츠 표시 여부를 결정하는 불리언 타입 isActive
state를 가진다.
import { useState } from 'react';
function Panel({ title, children }) {
const [isActive, setIsActive] = useState(false);
return (
<section className="panel">
<h3>{title}</h3>
{isActive ? (
<p>{children}</p>
) : (
<button onClick={() => setIsActive(true)}>
Show
</button>
)}
</section>
);
}
export default function Accordion() {
return (
<>
<h2>Almaty, Kazakhstan</h2>
<Panel title="About">
With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
</Panel>
<Panel title="Etymology">
The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
</Panel>
</>
);
}
그러나 이제 한 번에 하나의 패널만 열리도록 변경하려고 한다.
설계에 따르면, 두 번째 패널을 열기 위해선 첫 번째 패널을 닫아야 한다. 어떻게 해야 할까?
이 두 패널을 조정하려면 세 단계에 걸쳐 부모 컴포넌트로 “state를 끌어올려야” 한다.
이렇게 하면 Accordion
컴포넌트가 두 Panel
컴포넌트를 조정하고 한 번에 하나씩만 열리도록 할 수 있다.
1-1. 자식 컴포넌트에서 state 제거
Panel
의 isActive
를 제어할 수 있는 권한을 부여한다.isActive
를 Panel
에 prop으로 대신 전달하게 된다.Panel
컴포넌트에서 다음 줄을 제거해보자.const [isActive, setIsActive] = useState(false);
function Panel({ title, children, isActive }) {
Panel
의 부모 컴포넌트는 isActive
를 prop으로 전달하여 제어할 수 있다.Panel
컴포넌트는 isActive
값을 제어할 수 없다.1-2. 공통 부모에 하드 코딩된 데이터 전달하기
state를 끌어올리려면 조정하려는 두 자식 컴포넌트의 가장 가까운 공통 부모 컴포넌트를 찾아야 합니다:
Accordion
(가장 가까운 공통 부모)
Panel
Panel
예제에서 가장 가까운 공통 부모는 Accordion
컴포넌트이다.
두 패널 위에 있고 props를 제어할 수 있으므로 현재 어떤 패널이 활성화되어 있는지에 대한 “진실 공급원(source of truth)”이 된다.
Accordion
컴포넌트가 두 패널 모두에 하드 코딩된 isActive
값(예: true
)을 전달하도록 한다.
import { useState } from 'react';
export default function Accordion() {
return (
<>
<h2>Almaty, Kazakhstan</h2>
<Panel title="About" isActive={true}>
With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
</Panel>
<Panel title="Etymology" isActive={true}>
The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
</Panel>
</>
);
}
function Panel({ title, children, isActive }) {
return (
<section className="panel">
<h3>{title}</h3>
{isActive ? (
<p>{children}</p>
) : (
<button onClick={() => setIsActive(true)}>
Show
</button>
)}
</section>
);
}
1-3. 공통 부모에 state 추가
Accordion
는 어떤 패널이 활성화된 패널인지 추적해야한다.boolean
값 대신, 활성화된 Panel
의 인덱스를 나타내는 숫자를 state 변수로 사용할 수 있다.const [activeIndex, setActiveIndex] = useState(0);
activeIndex
가 0
이면 첫번째 패널이 활성화된 것이고, 1
이면 두 번째 패널이 활성화된 것이다.Panel
에서 “Show” 버튼을 클릭하면 Accordian
의 활성화된 인덱스를 변경해야 힌다.activeIndex
state가 Accordian
내부에 정의되어 있기 때문에 Panel
은 값을 직접 설정할 수 없다.Accordion
컴포넌트는 이벤트 핸들러를 prop으로 전달하여 Panel
컴포넌트가 state를 변경할 수 있도록 명시적으로 허용해야 한다.<>
<Panel
isActive={activeIndex === 0}
onShow={() => setActiveIndex(0)}
>
...
</Panel>
<Panel
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
...
</Panel>
</>
Panel
안에 있는 <button>
은 클릭 이벤트 핸들러로 onShow
prop을 사용할 수 있다.import { useState } from 'react';
export default function Accordion() {
const [activeIndex, setActiveIndex] = useState(0);
return (
<>
<h2>Almaty, Kazakhstan</h2>
<Panel
title="About"
isActive={activeIndex === 0}
onShow={() => setActiveIndex(0)}
>
With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
</Panel>
<Panel
title="Etymology"
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
</Panel>
</>
);
}
function Panel({
title,
children,
isActive,
onShow
}) {
return (
<section className="panel">
<h3>{title}</h3>
{isActive ? (
<p>{children}</p>
) : (
<button onClick={onShow}>
Show
</button>
)}
</section>
);
}
https://developer.mozilla.org/ko/