Closed zhangfisher closed 10 months ago
这个提案挺好的,本意是希望严格约束对象更新流程,方便变更记录可追溯,但是对于helux内部来说 reactive 和 setState 底层都是一套逻辑,是可以完美共存的,但提案的实现不太优雅,可能加的参数应该为 strictLevel 更合适,
最严格模式,只能使用 action提交变更(action自带描述)
strictLevel=1
表示使用响应式更新必须追加描述值,方便响应式变更也是可追踪到具体位置,如果没有没有desc,则变更操作不会提交(相当于改的草稿无效),开发模式给出报错。
reactive.a.b.c = 1;
reactiveDesc('xxxChange')
使用setState必须追加描述值,方便响应式变更也是可追踪到具体位置
setState((draft)=>reactive.a.b.c = 1, {desc:'xxxChange'});
松散模式,不限制任何变更操作
查看变更记录可参照这个示例,引入插件 然后写一个带有响应式描述的的变更操作
将会看到devtool有详细的变更记录可追溯
注:不添加描述的话,type为
xxxxModule@Reactive/setState
strictLevel还行
而reactive.a.b.c = 1; reactiveDesc('xxxChange') 分两句来写比较不可接受, 还是reactive.a.b.c=action()方式比较好,语义上也符合通过action来更新的含义
action 本身就是修改数据的逻辑,返回格式[nextSnap, err]
,强行和reative
结合在一起不符合设计, 目前defineActions
使用方式如下
// 【可选】约束各个函数入参 payload 类型
type Payloads = {
changeA: [number, number];
foo: boolean | undefined;
};
// 为方便提供各函数 payload 类型约束,这里采用柯里化方式
const { actions, useLoading, getLoading } = ctxp.defineActions<Payloads>()({
// 同步 action,直接修改草稿
changeA1({ draft, payload }) {
draft.a.b.c = 200;
},
// 同步 action,返回部分状态
changeA2({ draft, payload }) {
return { c: 'new desc' };
},
// 同步 action,直接修改草稿和返回部分状态同时使用
changeA3({ draft, payload }) {
draft.a.b.c = 200;
return { c: 'new desc' };
},
// 异步 action,直接修改草稿
async foo1({ draft, payload }) {
await delay(3000);
draft.a.b.c += 1000;
},
// 异步 action,多次直接修改草稿,返回部分状态
async foo2({ draft, payload }) {
draft.a.b.c += 1000;
await delay(3000); // 进入下一次事件循环触发草稿提交
draft.a.b.c += 1000;
await delay(3000); // 再次进入下一次事件循环触发草稿提交
const list = await fetchList();
return { list }; // 等价于 draft.list = list
},
});
// action 方法的异常默认被拦截掉不再继续抛出,只是并发送给插件和伴生loading状态
// 调用方法,错误会传递到 err 位置
const [ snap, err ] = actions.changeA1();
// 调用方法并抛出错误,此时错误既发给插件和伴生loading状态,也向上抛出,用户需自己 catch
const [ snap ] = actions.changeA1(1, true);
reactive
只表示数据修改,提交时机默认是下一次事件循环,
reactive.a.b.c =1;
reactive.a.b.c2 =2;
reactive.info = 'xxx';
await someMethod(); // 触发提交
可以主动提交
reactive.a.b.c =1;
flush(reactive, 'change_a.b.c'); // 主动提交,触发更新
reactive.a.b.c2 =2;
flush(reactive, 'change_a.b.c2'); // 主动提交,触发更新
reactive.info = 'xxx';
await someMethod(); // 由事件循环系统触发提交
理解完这个设计,你就明白 reacitve
接action
是不符合内部运行机制的,也不符合用户习惯
helux
在3.5.0
以后支持直接引入reactive
,可以支持直接进行响应式更新。如此要对一个
share
对象进行更新有三种方式:setState
action
reactive
setState
是对标React
的useState
的方法,action
是引入Flux
设计范式时导入的,Flux
是 React 框架的好伴侣。它优秀的单向数据流设计,使得数据的流向更加清晰,能帮助开发者更好的管理和调试组件的内部状态,因此在React
甚至Vue
的状态管理中被广泛使用。Flux
设计范式简单地说,就是不允许直接更改state
,而应该通过分发Action
来更新State
,这样可以使用State
的更新可预测,对大型对象状态很有好处。可以看到,对一个
share
对象的setState
和reactive
都是直接更新State
,明显不符合Flux
设计范式。setState
比较适合小型对象更新,此时无所谓设计模式。reactive
带来了无以伦比的灵活性和更新颗粒度,但是也破坏了Flux
设计范式,当我们一旦用了reactive
后,就会发现action
变得没有任何价值了,因为它打破了Flux
设计范式带来的强制约束。在其他
React
状态库中,如果你直接更新State
则是不允许的,并且会给出警告。本提案就是想为响应式对象引入严格模式,在严格模式下,对
reactive
的更新也会受到约束,让其在大型复杂对象下符合Flux
设计范式。本提案的思路如下:
share
时,通过strict=true
来指定严格模式action
来更新响应式对象在严格模式下,不再允许直接使用
reactive.a=1000
,而是只能通过action
来更新响应式对象。