參考文章
在組件間共享狀態(tài)
有時(shí)候,希望兩個(gè)組件的狀態(tài)始終同步更改。要實(shí)現(xiàn)這一點(diǎn),可以將相關(guān) state 從這兩個(gè)組件上移除,并把 state 放到它們的公共父級(jí),再通過 props 將 state 傳遞給這兩個(gè)組件。這被稱為“狀態(tài)提升”,這是編寫 React 代碼時(shí)常做的事。
狀態(tài)提升
在這個(gè)例子中,父組件 Accordion
渲染了 2 個(gè)獨(dú)立的 Panel
組件。
- Accordion
Panel
Panel
每個(gè) Panel
組件都有一個(gè)布爾值 isActive
,用于控制其內(nèi)容是否可見。
請(qǐng)點(diǎn)擊 2 個(gè)面板中的顯示按鈕:
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)}>
顯示
</button>
)}
</section>
);
}
export default function Accordion() {
return (
<>
<h2>哈薩克斯坦,阿拉木圖</h2>
<Panel title="關(guān)于">
阿拉木圖人口約200萬,是哈薩克斯坦最大的城市。它在 1929 年到 1997 年間都是首都。
</Panel>
<Panel title="詞源">
這個(gè)名字來自于 <span lang="kk-KZ">алма</span>,哈薩克語中“蘋果”的意思,經(jīng)常被翻譯成“蘋果之鄉(xiāng)”。事實(shí)上,阿拉木圖的周邊地區(qū)被認(rèn)為是蘋果的發(fā)源地,<i lang="la">Malus sieversii</i> 被認(rèn)為是現(xiàn)今蘋果的祖先。
</Panel>
</>
);
}
我們發(fā)現(xiàn)點(diǎn)擊其中一個(gè)面板中的按鈕并不會(huì)影響另外一個(gè),他們是獨(dú)立的。
假設(shè)現(xiàn)在想改變這種行為,以便在任何時(shí)候只展開一個(gè)面板。在這種設(shè)計(jì)下,展開第 2 個(gè)面板應(yīng)會(huì)折疊第 1 個(gè)面板。該如何做到這一點(diǎn)呢?”
要協(xié)調(diào)好這兩個(gè)面板,我們需要分 3 步將狀態(tài)“提升”到他們的父組件中。
- 從子組件中 移除 state 。
- 從父組件 傳遞 硬編碼數(shù)據(jù)。
- 為共同的父組件添加 state ,并將其與事件處理函數(shù)一起向下傳遞。
這樣,Accordion
組件就可以控制 2 個(gè) Panel
組件,保證同一時(shí)間只能展開一個(gè)。
第 1 步: 從子組件中移除狀態(tài)
將把 Panel
組件對(duì) isActive
的控制權(quán)交給他們的父組件。這意味著,父組件會(huì)將 isActive
作為 prop
傳給子組件 Panel
。先從 Panel
組件中 刪除下面這一行:
const [isActive, setIsActive] = useState(false);
然后,把 isActive
加入 Panel
組件的 props
中:
function Panel({ title, children, isActive }) {
現(xiàn)在 Panel
的父組件就可以通過 向下傳遞 prop 來 控制 isActive
。但相反地,Panel
組件對(duì) isActive
的值 沒有控制權(quán) —— 現(xiàn)在完全由父組件決定!
第 2 步: 從公共父組件傳遞硬編碼數(shù)據(jù)
為了實(shí)現(xiàn)狀態(tài)提升,必須定位到想?yún)f(xié)調(diào)的 兩個(gè) 子組件最近的公共父組件:
- Accordion(最近的公共父組件)
Panel
Panel
在這個(gè)例子中,公共父組件是 Accordion
。因?yàn)樗挥趦蓚€(gè)面板之上,可以控制它們的 props,所以它將成為當(dāng)前激活面板的“控制之源”。通過 Accordion
組件將硬編碼值 isActive
(例如 true
)傳遞給兩個(gè)面板:
import { useState } from 'react';
export default function Accordion() {
return (
<>
<h2>哈薩克斯坦,阿拉木圖</h2>
<Panel title="關(guān)于" isActive={true}>
阿拉木圖人口約200萬,是哈薩克斯坦最大的城市。它在 1929 年到 1997 年間都是首都。
</Panel>
<Panel title="詞源" isActive={true}>
這個(gè)名字來自于 <span lang="kk-KZ">алма</span>,哈薩克語中“蘋果”的意思,經(jīng)常被翻譯成“蘋果之鄉(xiāng)”。事實(shí)上,阿拉木圖的周邊地區(qū)被認(rèn)為是蘋果的發(fā)源地,<i lang="la">Malus sieversii</i> 被認(rèn)為是現(xiàn)今蘋果的祖先。
</Panel>
</>
);
}
function Panel({ title, children, isActive }) {
return (
<section className="panel">
<h3>{title}</h3>
{isActive ? (
<p>{children}</p>
) : (
<button onClick={() => setIsActive(true)}>
顯示
</button>
)}
</section>
);
}
可以嘗試修改 Accordion
組件中 isActive
的值,并在屏幕上查看結(jié)果。
第 3 步: 為公共父組件添加狀態(tài)
狀態(tài)提升通常會(huì)改變?cè)瓲顟B(tài)的數(shù)據(jù)存儲(chǔ)類型。
在這個(gè)例子中,一次只能激活一個(gè)面板。這意味著 Accordion
這個(gè)父組件需要記錄 哪個(gè) 面板是被激活的面板??梢杂脭?shù)字作為當(dāng)前被激活 Panel
的索引,而不是 boolean
值:
const [activeIndex, setActiveIndex] = useState(0);
當(dāng) activeIndex
為 0
時(shí),激活第一個(gè)面板,為 1
時(shí),激活第二個(gè)面板。
在任意一個(gè) Panel
中點(diǎn)擊“顯示”按鈕都需要更改 Accordion
中的激活索引值。 Panel
中無法直接設(shè)置狀態(tài) activeIndex
的值,因?yàn)樵摖顟B(tài)是在 Accordion
組件內(nèi)部定義的。 Accordion
組件需要 顯式允許 Panel
組件通過 將事件處理程序作為 prop 向下傳遞 來更改其狀態(tài):
<>
<Panel
isActive={activeIndex === 0}
onShow={() => setActiveIndex(0)}
>
...
</Panel>
<Panel
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
...
</Panel>
</>
現(xiàn)在 Panel
組件中的 <button>
將使用 onShow
這個(gè)屬性作為其點(diǎn)擊事件的處理程序:
import { useState } from 'react';
export default function Accordion() {
const [activeIndex, setActiveIndex] = useState(0);
return (
<>
<h2>哈薩克斯坦,阿拉木圖</h2>
<Panel
title="關(guān)于"
isActive={activeIndex === 0}
onShow={() => setActiveIndex(0)}
>
阿拉木圖人口約200萬,是哈薩克斯坦最大的城市。它在 1929 年到 1997 年間都是首都。
</Panel>
<Panel
title="詞源"
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
這個(gè)名字來自于 <span lang="kk-KZ">алма</span>,哈薩克語中“蘋果”的意思,經(jīng)常被翻譯成“蘋果之鄉(xiāng)”。事實(shí)上,阿拉木圖的周邊地區(qū)被認(rèn)為是蘋果的發(fā)源地,<i lang="la">Malus sieversii</i> 被認(rèn)為是現(xiàn)今蘋果的祖先。
</Panel>
</>
);
}
function Panel({
title,
children,
isActive,
onShow
}) {
return (
<section className="panel">
<h3>{title}</h3>
{isActive ? (
<p>{children}</p>
) : (
<button onClick={onShow}>
顯示
</button>
)}
</section>
);
}
這樣,就完成了對(duì)狀態(tài)的提升!將狀態(tài)移至公共父組件中可以讓你更好的管理這兩個(gè)面板。使用激活索引值代替之前的 是否顯示
標(biāo)識(shí)確保了一次只能激活一個(gè)面板。而通過向下傳遞事件處理函數(shù)可以讓子組件修改父組件的狀態(tài)。
受控組件和非受控組件
通常我們把包含“不受控制”狀態(tài)的組件稱為“非受控組件”。例如,最開始帶有 isActive
狀態(tài)變量的 Panel
組件就是不受控制的,因?yàn)槠涓附M件無法控制面板的激活狀態(tài)。
相反,當(dāng)組件中的重要信息是由 props
而不是其自身狀態(tài)驅(qū)動(dòng)時(shí),就可以認(rèn)為該組件是“受控組件”。這就允許父組件完全指定其行為。最后帶有 isActive
屬性的 Panel
組件是由 Accordion
組件控制的。
非受控組件通常很簡(jiǎn)單,因?yàn)樗鼈儾恍枰嗯渲谩5钱?dāng)你想把它們組合在一起使用時(shí),就不那么靈活了。受控組件具有最大的靈活性,但它們需要父組件使用 props
對(duì)其進(jìn)行配置。
在實(shí)踐中,“受控”和“非受控”并不是嚴(yán)格的技術(shù)術(shù)語——通常每個(gè)組件都同時(shí)擁有內(nèi)部狀態(tài)和 props
。然而,這對(duì)于組件該如何設(shè)計(jì)和提供什么樣功能的討論是有幫助的。
當(dāng)編寫一個(gè)組件時(shí),應(yīng)該考慮哪些信息應(yīng)該受控制(通過 props
),哪些信息不應(yīng)該受控制(通過 state
)。當(dāng)然,可以隨時(shí)改變主意并重構(gòu)代碼。
每個(gè)狀態(tài)都對(duì)應(yīng)唯一的數(shù)據(jù)源
在 React
應(yīng)用中,很多組件都有自己的狀態(tài)。一些狀態(tài)可能“活躍”在葉子組件(樹形結(jié)構(gòu)最底層的組件)附近,例如輸入框。另一些狀態(tài)可能在應(yīng)用程序頂部“活動(dòng)”。例如,客戶端路由庫(kù)也是通過將當(dāng)前路由存儲(chǔ)在 React
狀態(tài)中,利用 props
將狀態(tài)層層傳遞下去來實(shí)現(xiàn)的!
對(duì)于每個(gè)獨(dú)特的狀態(tài),都應(yīng)該存在且只存在于一個(gè)指定的組件中作為 state。這一原則也被稱為擁有 “可信單一數(shù)據(jù)源”。它并不意味著所有狀態(tài)都存在一個(gè)地方——對(duì)每個(gè)狀態(tài)來說,都需要一個(gè)特定的組件來保存這些狀態(tài)信息。應(yīng)該 將狀態(tài)提升 到公共父級(jí),或 將狀態(tài)傳遞 到需要它的子級(jí)中,而不是在組件之間復(fù)制共享的狀態(tài)。文章來源:http://www.zghlxwxcb.cn/news/detail-691127.html
你的應(yīng)用會(huì)隨著你的操作而變化。當(dāng)你將狀態(tài)上下移動(dòng)時(shí),你依然會(huì)想要確定每個(gè)狀態(tài)在哪里“活躍”。這都是過程的一部分!文章來源地址http://www.zghlxwxcb.cn/news/detail-691127.html
摘要
- 當(dāng)想要整合兩個(gè)組件時(shí),將它們的 state 移動(dòng)到共同的父組件中。
- 然后在父組件中通過
props
把信息傳遞下去。 - 最后,向下傳遞事件處理程序,以便子組件可以改變父組件的 state 。
- 考慮該將組件視為“受控”(由 prop 驅(qū)動(dòng))或是“不受控”(由 state 驅(qū)動(dòng))是十分有益的。
到了這里,關(guān)于React 18 在組件間共享狀態(tài)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!