Recoil 中默認值及數據間的依賴經過 Atom 可方便地設置數據的默認值,css const fontSizeState = atom({
key: 'fontSizeState',
default: 14,
});
而 Selector 可方便地設置數據的級聯依賴關係,即,另外一個數據可從現有數據進行派生。html const fontSizeLabelState = selector({
key: 'fontSizeLabelState',
get: ({get}) => {
const fontSize = get(fontSizeState);
const unit = 'px';
return `${fontSize}${unit}`;
},
});
結合這兩個特色,在實現數據間存在聯動的表單時,很是方便。react 一個實際的例子考察這樣的場景,購買雲資源時,會先選擇地域,根據所選地域再選擇該地域下的可用區。git 這裏就存在設置默認值的問題,未選擇時自動選中默認地域及對應地域下的默承認用區,也涉及數據間的級聯依賴,可選的可用區要根據地域而變化。github 呈現的效果以下:typescript 地域及可用區的選擇api 實現地域及可用區的選擇下面就經過 Recoil 來實現上述地域及可用區的選擇邏輯。markdown 建立示例項目$ yarn create react-app recoil-nest-select --template typescript
添加並使用 Recoil安裝依賴:app $ yarn add recoil
使用 Recoil, 首先將應用包裹在 index.tsx import { RecoilRoot } from "recoil";
ReactDOM.render(
<React.StrictMode>
<RecoilRoot>
<Suspense fallback="loading...">
<App />
</Suspense>
</RecoilRoot>
</React.StrictMode>,
document.getElementById("root")
);
添加 appState.ts interface IZone {
id: string;
name: string;
}
interface IRegion {
id: string;
name: string;
zones: IZone[];
}
添加假數據根據上面定義的類型,添加假數據: mock.ts export const mockRegionData = [
{
id: "beijing",
name: "北京",
zones: [
{
id: "beijing-zone-1",
name: "北京一區",
},
{
id: "beijing-zone-2",
name: "北京二區",
},
{
id: "beijing-zone-3",
name: "北京三區",
},
],
},
{
id: "shanghai",
name: "上海",
zones: [
{
id: "shanghai-zone-1",
name: "上海一區",
},
{
id: "shanghai-zone-2",
name: "上海二區",
},
{
id: "shanghai-zone-3",
name: "上海三區",
},
],
},
{
id: "guangzhou",
name: "廣州",
zones: [
{
id: "guangzhou-zone-1",
name: "廣州一區",
},
{
id: "guangzhou-zone-2",
name: "廣州二區",
},
],
},
];
添加狀態數據添加地域及可用區狀態數據,先看地域數據,該數據用來生成地域的下拉框。真實狀況下,該數據來自異步請求,這裏經過 Promise 模擬異步數據。 appState.ts import { atom, selector } from "recoil";
import { mockRegionData } from "./mock";
export const regionsState = selector({
key: "regionsState",
get: ({ get }) => {
return Promise.resolve<IRegion[]>(mockRegionData);
},
});
添加一個狀態用於保存當前選中的地域: appState.ts export const regionState = atom({
key: "regionState",
default: selector({
key: "regionState/Default",
get: ({ get }) => {
const regions = get(regionsState);
return regions[0];
},
}),
});
這裏經過使用 atom 並指定默認值爲地域第一個數據,達到下拉框默認選中第一個的目的。 添加地域選擇組件添加地域選擇組件,使用上面建立的地域數據。 RegionSelect.tsx import React from "react";
import { useRecoilState, useRecoilValue } from "recoil";
import { regionsState, regionState } from "./appState";
export function RegionSelect() {
const regions = useRecoilValue(regionsState);
const [region, setRegion] = useRecoilState(regionState);
return (
<label htmlFor="regionId">
地域:
<select
name="regionId"
id="regionId"
value={region.id}
onChange={(event) => {
const regionId = event.target.value;
const region = regions.find((region) => region.id === regionId);
setRegion(region!);
}}
>
{regions.map((region) => (
<option key={region.id} value={region.id}>
{region.name}
</option>
))}
</select>
</label>
);
}
至此地域部分完成,可用區同理,只不過可用區的拉下數據依賴於當前選中的地域。 添加可用區狀態數據及下拉組件appState.tsx export const zonesState = selector({
key: "zonesState",
get: ({ get }) => {
const region = get(regionState);
return region.zones;
},
});
export const zoneState = atom({
key: "zoneState",
default: selector({
key: "zoneState/default",
get: ({ get }) => {
return get(zonesState)[0];
},
}),
});
可選擇的可用區依賴於當前選中的地域,經過 可用區的默認值也是拿到當前可選的全部地域,而後取第一個, ZoneSelect.tsx import React from "react";
import { useRecoilState, useRecoilValue } from "recoil";
import { zonesState, zoneState } from "./appState";
export function ZoneSelect() {
const zones = useRecoilValue(zonesState);
const [zone, setZone] = useRecoilState(zoneState);
return (
<label htmlFor="zoneId">
可用區:
<select
name="zoneId"
id="zoneId"
value={zone.id}
onChange={(event) => {
const zoneId = event.target.value;
const zone = zones.find((zone) => zone.id === zoneId);
setZone(zone!);
}}
>
{zones.map((zone) => (
<option key={zone.id} value={zone.id}>
{zone.name}
</option>
))}
</select>
</label>
);
}
展現當前地域及可用區將前面兩個下拉框展現出來,同時展現當前地域及可用區。 App.tsx import React from "react";
import { useRecoilValue } from "recoil";
import "./App.css";
import { regionState, zoneState } from "./appState";
import { RegionSelect } from "./RegionSelect";
import { ZoneSelect } from "./ZoneSelect";
function App() {
const region = useRecoilValue(regionState);
const zone = useRecoilValue(zoneState);
return (
<div className="App">
<p>region:{region.name}</p>
<p>zone:{zone.name}</p>
<RegionSelect />
<ZoneSelect />
</div>
);
}
export default App;
至此完成了整個程序的實現。 最終效果來看看效果: 地域及可用區聯動效果 帶默認值的狀態未自動更新的問題上面的實現乍一看實現了功能,但進行可用區的選擇以後問題便會暴露。 可用區未聯動的問題 能夠看到可用區更新後,再切換地域,雖然下拉框中可選的可用區更新了,但實際上當前可用區的值停留在了上一次選中的值,並無與地域聯動。若是不是把可用區展現出來,不容易發現這裏的問題,具備必定迷惑性。 看看可用區下拉值 export const zonesState = selector({
key: "zonesState",
get: ({ get }) => {
const region = get(regionState);
return region.zones;
},
});
由於可用區是從當前選中的地域數據 再看看當前選中的可用區 export const zoneState = atom({
key: "zoneState",
default: selector({
key: "zoneState/default",
get: ({ get }) => {
return get(zonesState)[0];
},
}),
});
它經過 當切換地域時, 當咱們手動進行了可用區選擇時,在可用區下拉組件中, <select
name="zoneId"
id="zoneId"
value={zone.id}
+ onChange={(event) => {
+ const zoneId = event.target.value;
+ const zone = zones.find((zone) => zone.id === zoneId);
+ setZone(zone!);
}}
>
{zones.map((zone) => (
<option key={zone.id} value={zone.id}>
{zone.name}
</option>
))}
</select>
手動添加依賴直接的修復方式能夠在可用區組件中監聽地域的變化,當地域變化後,設置一次可用區。 export function ZoneSelect() {
+ const region = useRecoilValue(regionState);
const zones = useRecoilValue(zonesState);
const [zone, setZone] = useRecoilState(zoneState);
+ console.log("zone:", zone.id);
+ useEffect(() => {
+ setZone(zones[0]);
+ }, [region]);
return (
<label htmlFor="zoneId">
…
</label>
);
}
能達到目的,但經過打印出來的可用區值來看,當地域切換後,可用區的值更新並不及時,首先會打印出一個錯誤的值,待 經過 `useEffect` 方式來修正,可用區更新會滯後 |
The text was updated successfully, but these errors were encountered: |