回到 selectorFamily 提供的當初的情境。
Codesandbox demo
簡單假設我們的api格式
export function getBanks() {
return [
{ id: 0, name: "中國信託" },
{ id: 1, name: "玉山銀行" },
{ id: 2, name: "王道銀行" },
{ id: 3, name: "富邦銀行" },
{ id: 4, name: "國泰銀行" }
];
}
export function getBranches(id) {
let branches = [
[
{ id: 0, name: "中信分行1" },
{ id: 1, name: "中信分行2" },
{ id: 2, name: "中信分行3" },
{ id: 3, name: "中信分行4" }
],
........
[
{ id: 16, name: "國泰分行1" },
{ id: 17, name: "國泰分行2" },
{ id: 18, name: "國泰分行3" },
{ id: 19, name: "國泰分行4" }
]
];
return branches[id];
}
從 UI 下手去思考,為了符合需求,我們建立了兩組 select / option ,一組是銀行選項,另一組是根據該銀行顯示分行的選項。
<>
銀行:
<select onChange={updateBank}>
{bankOptions.map((bank) => (
<option key={bank.id} value={bank.id}>
{bank.name}
</option>
))}
</select>
<br />
分行:
<select onChange={updateBranch}>
{branchOptions.map((branch, index) => (
<option key={branch.id} value={branch.id}>
{branch.name}
</option>
))}
</select>
</>
因此回過頭來思考,我們需要
1.一個狀態來儲存所有銀行的選項
2.一個狀態來儲存當前被選到的銀行
3.一個狀態儲存當前被選到的銀行底下分行的選項
4.一個狀態儲存當前分行的選項,但是切換銀行時,我們希望這個分行也會被 Reset。
export const bankOptionState = atom({
key: "bankOption",
default: getBanks()
});
儲存當前銀行ID
export const currentBankIDState = atom({
key: "currentBankID",
default: 0
});
首先我們希望所有的分行資訊都能被 Recoil 儲存,但又不希望開很多key 來儲存很多 state (比如 bank1branchOption、bank2branchOption、bank3branchOption、bank4branchOption),因此我們透過 selectorFamily 將所有 branch 都用同一個 key branchOption 儲存起來,我們在帶入 bank id 來取得對應的 branch。
export const branchOptionState = selectorFamily({
key: "branchOption",
get: (bankID) => ({ get }) => {
const branches = getBranches(bankID);
return branches;
}
});
儲存當前分行ID
export const currentBranchIDState = atom({
key: "currentBranchID",
default: 0
});
首先需要取讀 currentBankID, currentBranchID 再據此渲染對應的option
function SomeUserForm() {
const [currentBankID, setCurrentBankID] = useRecoilState(currentBankIDState);
const [currentBranchID, setCurrentBranchID] = useRecoilState(
currentBranchIDState
);
const updateBank = ({ target: { value } }) => {
setCurrentBankID(value);
};
const updateBranch = ({ target: { value } }) => {
setCurrentBranchID(value);
};
接著取得對應的 option,而這邊的 branchOptions ,是透過selectorFamily所建立,可以傳入參數銀行ID,因此可根據當前銀行ID 傳回對應的分行 option
const bankOptions = useRecoilValue(bankOptionState);
const branchOptions = useRecoilValue(branchOptionState(currentBankID));
接著完成我們的元件
<>
銀行:
<select value={currentBankID} onChange={updateBank}>
{bankOptions.map((bank) => (
<option key={bank.id} value={bank.id}>
{bank.name}
</option>
))}
</select>
<br />
分行:
<select value={currentBranchID} onChange={updateBranch}>
{branchOptions.map((branch, index) => (
<option key={branch.id} value={branch.id}>
{branch.name}
</option>
))}
</select>
</>
但目前我們的銀行改變,還不會跟著修改分行ID,因此我們加入一組useEffect,讓銀行ID改變時,也改變分行
useEffect(() => {
setCurrentBranchID(branchOptions[0].id);
}, [currentBankID]);