前情回顾

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

场景是:

  • 地域下拉决定可选的可用区
  • 默认选中第一个地域,通过设置 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底层技术如何助力SPC子币VAST高价与安全并行?

    NGK近来使用了新的侧链技术推出了新的SPC侧链代币,以及SPC的子币VAST---维萨币. NGK使用去中心化和开源区块链数据分布式协议,不断打造高倍币,力求成为生态建设参与者们所信赖的高倍币孵化器 ...

  2. HTML页面顶部出现空白部分(#65279字符?)解决办法

    1.在火狐下面用Firebug,选择body,点编辑html的时候,看到是多出了一个这个代表的意思,还真不知道,搜索后了解到是一种中文的编码规则,   UTF-8不需要BOM来表明字节顺序.   制作 ...

  3. Python数据结构与算法_反转字符串(08)

    编写一个函数,其作用是将输入的字符串反转过来.输入字符串以字符数组 char[] 的形式给出. 不要给另外的数组分配额外的空间,你必须原地修改输入数组.使用 O(1) 的额外空间解决这一问题. 你可以 ...

  4. java: 类 RegisterController 是公共的, 应在名为 RegisterController.java 的文

    public声明的类名需要和文件名一致,检查一下

  5. Mybatis-Plus插件配置

    yml配置 1 # Mybatis-Plus 2 mybatis-plus: 3 # 配置mapper的扫描,找到所有的mapper.xml映射文件 4 mapper-locations: com.x ...

  6. Hyperf-事件机制+异常处理

    Hyperf-事件机制+异常处理 标签(空格分隔): php, hyperf 异常处理器 在 Hyperf 里,业务代码都运行在 Worker 进程 上,也就意味着一旦任意一个请求的业务存在没有捕获处 ...

  7. 第35天学习打卡(输入框 TextField监听 简易计算器,组合+内部类回顾复习 画笔 鼠标监听 窗口监听 键盘监听)

    1.输入框 TextField监听  package com.kuang.lesson02; ​ import java.awt.*; import java.awt.event.ActionEven ...

  8. 破解 Android 上 airpods 连接软件的pro版

    0x00 起因 起因是在Android上用了一段时间的AndPods觉得不太好用之后,换到了另一个Play商店推荐的App.动画.连接和电量提示都用的很满意,就是每次连接的弹窗和APP里面都有广告,就 ...

  9. 【转载】Android的事件分发(dispatchTouchEvent),拦截(onInterceptTouchEvent)与处理(onTouchEvent)

    出处:https://blog.csdn.net/caifengyao/article/details/65437695 在Android中,View的结构是树状的,所以,当触发触摸事件的时候,其事件 ...

  10. 完全使用 VSCode 开发的心得和体会

    前言 我刚开始是一名 Java 程序员,陪伴我最久的老伙计是 Java 世界里面出名好用的是 Jetbrains 家族的重量级产品 Intelli IDEA 编辑器,不过 IDEA 主要是用来写代码, ...