Photo by Hello I’m Nik on Unsplash
上一章提到,父容器可以把state和handlers作為參數,傳給children。 這一章則要進一步說明,如果children當中,有部分的元件與父容器的相依性很高,例如要呼叫父容器的function進行一連串動作,那麼父容器與子元件之間該如何合作,以最少的程式碼降低元件之間的耦合度。
3. Prop Getters and Render Props 父爸爸的大秘寶怎麼用,更好用
| 使用場景:在父容器的children當中,有部分的子元件會呼叫父容器的function作一系列的動作,這種子元件與父容器的相依性很高。
以下是我在實務上的應用場景:
範例說明: 這是一個彈出視窗,裡面可能有多個下拉式選單。 下拉式選單具有「多選」的功能。
下拉式選單的右邊,有個漏斗的按鈕。點擊後,會彈出第二個彈出視窗。
第二個彈出視窗的主要功能,是讓使用者可以選擇「區間」。例如編號1號~5號,20號~30號,以此類推。
選擇完區間後,按下「OK」,方才選擇的區間項目就會出現在下拉式選單上。
每個沙漏按鈕,都會彈出「區間選擇視窗」,並將選定的區間交還給最初的下拉式選單。差別只在於List的不同。 因此,這種重複性的邏輯,就交給彈出視窗的元件統一處理。
按照上一章的思路,
const FilterModal = () => {
// ...
const openChooseBtwModalHandler = (arr, ...props) => {
// get array
// get required info
// open Modal
}
const getStateAndHelpers = () => {
return {
openChooseBtwModalHandler: openChooseBtwModalHandler };
};
return (
<>
<div className="modalContent">
{children(getStateAndHelpers())}
</div>
</>
);
}
const WrappedDemo = () => {
// ...
return {
<FilterModal>
{({ openChooseBtwModalHandler }) => ( // ...
<Select />
<Button onClick={() => openChooseBtwModalHandler({
arr: list,
// ...others
})} />
)}
</FilterModal>
}
}
先看<WrappedDemo />
元件裡面,<Button />
的部分。Button的onClick事件會呼叫由父容器<FilterModal />
傳下來的openChooseBtwModalHandler()
(打開區間選擇視窗)。呼叫後,交由<FilterModal />
去處理內部的邏輯。
上面的思路沒什麼問題。
不過假如有一天,Button除了onClick事件,還需要透過邏輯判斷是否要disabled,或因為需要call其它API,在loading時增添CSS動畫。這麼一來,程式碼需要改的範圍就很大了,有多少個Button,就要改多少地方。
如果這個Button的功能,與父容器<FilterModal />
的相依性很高,那麼更好的做法是,把Button的屬性,交給父容器去處理。思路如下:
const FilterModal = () => {
// ...
const toggleChooseBtwModal = () => {
// open Modal
}
const getChooseBtwData = (arr, ...props) => { // return props for Button
return { ...props, onClick: () => { // do something after onClick toggleChooseBtwModal() } // ...other custom properties // ex: disabled={isDisabled} } }
const getStateAndHelpers = () => {
return {
getChooseBtwData: getChooseBtwData
};
};
return (
<>
<div className="modalContent">
{children(getStateAndHelpers())}
</div>
</>
);
}
const WrappedDemo = () => {
// ...
return {
<FilterModal>
{({ getChooseBtwData }) => ( // ...
<Select />
<Button {...getChooseBtwData({ arr: list // ...others })} /> )}
</FilterModal>
}
}
先看<WrappedDemo />
元件裡面,<Button />
的部分,會注意到Button在這裡並沒有顯示onClick的行為,只能看見...getChooseBtwData()
掌管了Button的props。而getChooseBtwData
這個方法是由父容器<FilterModal />
所提供的。
接著往上看父容器<FilterModal />
的程式碼,其中的函式getChooseBtwData
回傳了onClick屬性。這裡保留了Button屬性的增減空間,例如增加disabled、style等屬性。
以下是本章節的範例程式碼:
這種設計模式在Ant.Design的元件中時常看到,例如本章在CodeSandBox當中的範例程式碼05.js,可以看見Ant.Design的getFieldDecorator
就是使用這種方式,既能增加套件的功能,又同時減少code farmer所需撰寫的程式碼。
Summary
- 如果父容器的children當中,有部分的元件與父容器有很高的相依性,例如要呼叫父容器的函式來進行一系列的動作,那麼,讓父容器直接掌管這個子元件的props,會是比較好的選擇。
- 父容器可以return這類型的子元件所需要的props,透過render props傳給children。
- 子元件可以用spread operator(…)將所需的props展開來,同時也可以增添特別規格的props,讓父容器一併執行。