前情回顾

书接上回,前面引出了在数据存在级联的情况下,各下拉框之间的默认值及值变化的处理。简单回顾一下:

场景是:

  • 地域下拉决定可选的可用区
  • 默认选中第一个地域,通过设置 atomdefault 字段
  • 默认选中该地域下第一个可用区,通过设置 atomdefault 字段

问题:

  • 手动选择一下可用区,此时更新了可用区的值
  • 手动选择一下地域,此时更新了地域,可用区下拉框同步更新,此时实际可用区的值为前面手动选择的旧值,界面上却展示的新可用区的第一个。

解决:

  • 在地域选择组件中,当地域发生变化时,重置一下可用区使其回到默认值。

新的问题

进一步实践,会发现这种解决方式存在缺陷,在多级级联的情况下,比如三个下拉框 A->B->C,A 决定 B, B 决定 C,按照这个解决思路,

  • 在 A 变化时需要重置 B,C
  • 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 执行完后才会打印正确的值。如果在旧值的情形下依赖该状态去做了些业务逻辑,势必会导致错误,比如拿这个旧值去发起请求。

状态的正确使用

细思会发现,上面之所以会有这种错误是因为姿势没对,假若我们要使用可用区的值,应该在 useEffect 中进行,亦即:

  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>
);
}

优化数据的依赖关系

进一步思考,导致可用区需要重置的直接原因其实并不是地域发生了变化,而是地域发生变化后,可用区下拉框的可选项发生了变化,亦即 zonesState。既然下拉选项变化了,当然需要重置默认值为新的下拉选项中的第一个。所以可用区组件中直接监听下拉选项,而非地域。

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>
);
}

这样一来,组件内部就清爽多了,只有自身相关的数据,甚至都去掉了对 regionState 的使用。

selector 派生数据的隐形桥梁功能

这里其实是 zonesState 作为桥梁自动完成了对 region 的监听,因为 zonesStateselector,它是从 regionState 派生出来的数据,在 regionState 发生变化时,会由 Recoil 负责更新。

其他

最后,示例代码参见 wayou/recoil-nest-select

The text was updated successfully, but these errors were encountered:

Recoil 中多级数据联动及数据重置的合理做法的更多相关文章

  1. easyui datagrid行编辑中数据联动

    easyui的datagrid中行内编辑使用数据联动.即:当编辑产品编号时,该行的产品名称自动根据产品编号显示出来. 在编辑中获取当前行的索引 function getRowIndex(target) ...

  2. BPM配置故事之案例14-数据字典与数据联动

    小明遇到了点麻烦,他昨天又收到了行政主管发来的邮件,要求把出差申请单改由H3 BPM进行,表单如下 行政主管的出差申请表 小明对表单进行了调整,设计出了一份适合在系统中使用的表单,但在"出差 ...

  3. pentaho cde数据联动,下拉框,文本框,图形

    先看一下效果: 开源bi工具pentaho数据联动,和传统意义上的更改数据不同,pentaho cde 需要一个监听来动态传值. 说一下需要注意的几个地方吧 1.参数是不能在两个图表中直接传递的,必须 ...

  4. avalon2学习教程11数据联动

    在许多表单应用,我们经常遇到点击一个复选框(或下拉框)会引发旁边的复选框(或下拉框)发生改变,这种联动效果用avalon来做是非常简单的.因为avalon拥有经典MVVM框架的一大利器,双向绑定!绝大 ...

  5. TreeView和ListView数据库查询数据联动操作

    好久不用了,重新整理下放这里以备需要使用,功能见图 数据库表结构 定义TreeView addObject中data存储的记录集 type PNode = ^TNode; TNode = record ...

  6. Hadoop 中利用 mapreduce 读写 mysql 数据

    Hadoop 中利用 mapreduce 读写 mysql 数据   有时候我们在项目中会遇到输入结果集很大,但是输出结果很小,比如一些 pv.uv 数据,然后为了实时查询的需求,或者一些 OLAP ...

  7. C#开发微信门户及应用(14)-在微信菜单中采用重定向获取用户数据

    我曾经在系列文章中的<C#开发微信门户及应用(11)--微信菜单的多种表现方式介绍>中介绍了微信菜单里面的重定向操作,通过这个重定向操作,我们可以获取一个code值,然后获取用户的open ...

  8. TCP/IP中链路层的附加数据(Trailer数据)和作用

    1.TCP/IP中链路层的附加数据是什么 在用wireshark打开报文时,链路层显示的Trailer数据就是附加数据,如图 2.如何产生 1.例如以太网自动对小于64字节大小的报文进行填充(未实验) ...

  9. asp.net三层架构 及其中使用泛型获取实体数据介绍

    asp.net中使用泛型获取实体数据可以发挥更高的效率,代码简洁方便,本例采用三层架构.首先在model层中定义StuInfo实体,然后在 DAL层的SQLHelper数据操作类中定义list< ...

随机推荐

  1. NGK全球巡回路演莫斯科站,共探BGV能否超越YFI?

    近日,NGK全球巡回路演在俄罗斯首都莫斯科落下帷幕.此次路演取得了空前的成功.路演伊始俄罗斯路演讲师Andrew致开幕辞,安德鲁称,俄罗斯作为未一个幅员辽阔的大国,区块链技术有着非常大的应用场景. 俄 ...

  2. net面试总结的题目

    准备的面试题目. 1.private.protected.public.internal的访问权限? private : 私有成员,在类的内部才可以访问. protected :保护成员,该类内部和继 ...

  3. OAuth2理解

    OAuth2: 1.他是针对特定问题的一种解决方案 OAuth2主要可以解决两个问题: (1)开放系统间的授权问题 例如:当你想要一个打印机器去打印自己百度网盘中的照片时,该机器并没有访问自己百度网盘 ...

  4. js的基本数据类型与引用数据类型

    基本数据类型与引用数据类型 基本数据类型有五种 /* 基本数据类型有: - String - Number - Boolean - Null ** typeof null === 'object' 这 ...

  5. xmake v2.5.2 发布, 支持自动拉取交叉工具链和依赖包集成

    xmake 是一个基于 Lua 的轻量级跨平台构建工具,使用 xmake.lua 维护项目构建,相比 makefile/CMakeLists.txt,配置语法更加简洁直观,对新手非常友好,短时间内就能 ...

  6. Snort + Barbyard2 + Snorby环境搭建

    1.环境 ubuntu-14.04.5 daq-2.0.7 Snort-2.9.15.1 Barbyard2 snorby Mysql Docker 2.架构 3.安装步骤 Ubuntu配置 如果是刚 ...

  7. IDEA SVN 使用

    转: IDEA SVN 使用 一.上传项目到 SVN VCS -> Import into Version Control -> Share Project(Subversion) 点击 ...

  8. PAT-1140(Look-and-say Sequence)字符串处理

    Look-and-say Sequence PAT-1140 #include<iostream> #include<cstring> #include<string&g ...

  9. C语言中指针和多维数组

    指针和多维数组 数组名是特殊的指针 数组是一个特殊的指针,多维数组也是更为复杂的数组,它们的关系是什么样的呢? 我们通过一个简单的例子来比较形象的了解指针和多维数组: int a[2][3]; 这是一 ...

  10. Graylog日志管理单机部署、日志节点的Sidecar配置以及简单的警告事件邮件发送

    应该是上个星期的上个星期,下了个任务,要做Graylog的部署以及文档,emmm....带log,肯定是和日志有关系了呗,不过也没听过啊,去搜了一下,确实,也不少帖子博客相关的都有安装部署,还是yum ...