為什麼我們需要 selectorFamily ? 還記得我們在前面幾堂介紹 atom / selector 如何使用嗎?
我們使用一個 atom 來記錄一個共享狀態「長度」,另外透過 2 個 selector 根據長度變化來計算正方形面積與圓面積。
今天我們將長度存入 Recoil 內,可以直接透過 selector 獲取任何在 Recoil 之內的狀態。在現實應用上,我們有時候不能或不想將某個狀態存入 Recoil ,但又希望某個共享狀態是可以根據外部的狀態做出對應的計算,我們預期以參數的形式帶入 Recoil 讓 Recoil 根據這個參數作出對應的運算、互動,這時候我們就需要 selectorFamily
Codesandbox demo(atom / selector)
const lengthState = atom({
key: "length",
default: 0
});
const circularAreaState = selector({
key: "circularArea",
get: ({ get }) => {
return get(lengthState) * get(lengthState) * 3.14;
}
});
今天假如我們不想把這個 length 長度存入 Recoil,改依賴元件內部 local 狀態,我們可以先把 pi 改成 Recoil 內部共享的 shared state,然後再透過外部帶入半徑的數字,讓 Recoil 內,可以取得外部的 半徑 以及內部共享的 pi。此時我們透過 selectorFamily ,將selector 的 get 屬性 ({get})=>compute(recoil state)
改成 (param)=>({get})=>compute(recoil state with param)
。
示意如下:
Codesandbox demo(selectorFamily)
const piState = atom({
key: "pi",
default: 3.14
});
const circularAreaState = selectorFamily({
key: "circularArea",
get: (length) => ({ get }) => {
return get(piState) * length * length;
}
});
此時,我們在元件內呼叫 circularAreaState 時,我們一樣是透過 useRecoilValue ,只是此時,我們就可以在我們的 state 後面帶上參數 const circularArea = useRecoilValue(circularAreaState(10));
,這樣子表示我們帶入參數 10 ,而根據我們在 selectorFamily 內部的定義,我們將會拿 10 * 10 * pi,得到以 10 為半徑的圓面積。
接著,我們將原本 Recoil 共享的shared state 長度 (length) 改為 local state,並將這個 local state 作為參數帶入 Recoil 當中。
const MyComp = () => {
const [length, setLength] = useState(1);
const circularArea = useRecoilValue(circularAreaState(length));
const handleChange = (e) => {
setLength(e.target.value);
};
return (
<div>
{`請修改長度`}
<input type="number" onChange={handleChange} />
<div>{` 長度 is ${length}`}</div>
<div>{` 圓面積 is ${circularArea}`}</div>
</div>
);
};
Codesandbox demo(selectorFamily)