Recoil 中多级数据联动及数据重置的合理做法
前情回顾书接上回,前面引出了在数据存在级联的情况下,各下拉框之间的默认值及值变化的处理。简单回顾一下: 场景是:
问题:
解决:
新的问题进一步实践,会发现这种解决方式存在缺陷,在多级级联的情况下,比如三个下拉框 A->B->C,A 决定 B, B 决定 C,按照这个解决思路,
这显然不科学,非常冗余。同时从组件解耦的角度来看,A,B 需要知道谁依赖了自己从而重置它们,这种耦合非常难以维护。 因此应该反过来,将解决问题的逻辑囿于组件自身才是科学的做法。 于是 A 不管其他,只管自己随便随便怎么变化,B 中监听 A 变化然后做出反应以重置自己,C 监听 B 的变化以重置自己。这样逻辑做到了内聚无耦合。 而之前文章中之所以没用这种方式,是因为发现该方式具有滞后性,组件内部会停留在错误的值上渲染一次。 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(() => {
// do sth with zone
console.log("zone", zone.id);
}, [zone]);
此时打印就会得到正确的结果。 按照这个逻辑修正后的组件及联动关系就成了: RegionSelect.tsx export function RegionSelect() {
const regions = useRecoilValue(regionsState);
const [region, setRegion] = useRecoilState(regionState);
return (
<label htmlFor="regionId">
Region:
<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.id}
</option>
))}
</select>
</label>
);
}
ZoneSelect.tsx export function ZoneSelect() {
const zones = useRecoilValue(zonesState);
const [zone, setZone] = useRecoilState(zoneState);
const resetZone = useResetRecoilState(zoneState);
const region = useRecoilValue(regionState);
// region 变化后重置 zone
useEffect(() => {
resetZone();
}, [region, resetZone]);
useEffect(() => {
// do sth with zone
console.log("zone", zone.id);
}, [zone]);
return (
<label htmlFor="zoneId">
Zone:
<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.id}
</option>
))}
</select>
</label>
);
}
优化数据的依赖关系进一步思考,导致可用区需要重置的直接原因其实并不是地域发生了变化,而是地域发生变化后,可用区下拉框的可选项发生了变化,亦即 export function ZoneSelect() {
const zones = useRecoilValue(zonesState);
const [zone, setZone] = useRecoilState(zoneState);
const resetZone = useResetRecoilState(zoneState);
useEffect(() => {
resetZone();
}, [resetZone, zones]);
useEffect(() => {
// do sth with zone
console.log("zone", zone.id);
}, [zone]);
return (
<label htmlFor="zoneId">
Zone:
<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.id}
</option>
))}
</select>
</label>
);
}
这样一来,组件内部就清爽多了,只有自身相关的数据,甚至都去掉了对
|
The text was updated successfully, but these errors were encountered: |