學習完 state 構築的原則,來進一步看 state 的應用,今天要學習如何在元件間共用 state 來達到更精準的控制權責關係。我們會透過一個手風琴(Accordion)的例子來練習。
這段程式碼呈現了一個可展開的面板效果。它包括兩個主要元件:Panel 和 Accordion。每個 Panel 組件代表一個可展開的面板,具有標題和內容。Accordion 元件是主要的容器,包含了兩個 Panel 子組件,它們代表了不同主題的內容。當用戶點擊「Show」按鈕時,對應的面板會展開,顯示相應的內容。
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 和 Panel 元件,讓使用者能在二個面板操作時都能顯示對應的畫面,而二個面板皆為彼此獨立的。
若是我們要做更近一步的設定,「打開A面板的時候B面板要顯示關閉,打開B面板的時候,A要顯示關閉」的話,該如何操作呢?
我們要用到名為「lift their state up」的操作,這個操作有以下三個步驟:
斷開 Panel 元件和isActive
的連結,先將這行移除
const [isActive, setIsActive] = useState(false);
將isActive
作為 props 傳遞到 Panel 元件當中。
function Panel({ title, children, isActive })
像這樣Panel
的父元件為Accordion
而isActive
作為硬編碼來傳遞到每個Panel
元件:
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>
);
}
硬編碼指的是程式碼中直接指定固定數據或值,而不是從外部來源或設定中動態獲取。這些固定的數據或值通常直接寫入程式碼,並且在程式運行時不會變化。譬如像是在這個程式碼當中,硬編碼指的是title
和isActive
。
首先在父元件 Accordion 當中追加狀態變數的設定,這次我們不用 true 和 false,而是採用 0 和 1 來處理,這邊要留意的是 0 或 1 不代表開或者關,而是代表了面板的索引編號。
const [activeIndex, setActiveIndex] = useState(0);
使用者按下按鈕時則會觸發activeIndex
的設定,第一個 ActiveIndex 為 0,第二個 ActiveIndex 則是 1。當第一個面板處於活躍(展開)狀態時,activeIndex
將是 0;當第二個面板處於活躍(展開)狀態時,activeIndex
將是 1。也就是說每個 activeIndex
的值都對應到一個特定的面板。
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>
);
}
透過 isActive
的真偽值來決定手風琴的開合是正確的。isActive
是一個布林值,它決定了面板是否處於活躍(展開)狀態。由於兩個面板共用相同的 activeIndex
狀態變數,所以當其中一個按鈕被點擊時,會改變 activeIndex
的值,進而影響 isActive
的真偽狀態,決定了面板的開合狀態。這樣的設計實現了手風琴效果,當一個面板打開時,另一個面板將自動關閉。
今天學習的內容主要的一個核心概念是「誰在控制」,譬如我們將狀態變數移至父元件這件事,本身就是在釐清一個權責關係。透過交付這樣子的控制權,而達到了「切換」功能的動態性 accordion。