背景(轻喷)
在我的日常开发中,至少80%的数据都有明确的消费者(全局的数据除外,比如用户信息,我也不知道啥时候会被谁用到),而且大多数数据都会具有时效性(使用完一次之后就没用了)。
最简单的例子就是表单查询,查询的信息在拉取到想要的接口信息后,一般来说就没用了。但是在实际的开发中,我们会被迫地去保存这个值,因为查询组件和表格组件可能不在一个文件(组件)中,我没法直接把我的searchValue传递给table,我真的受够了因此带来的困扰,要设计props, onValueChange,还要把状态提升到父组件,或者去写个context去解决这个明明看起来很简单的数据传递。
为什么 table 不能去直接订阅 search 的值呢,所以就有了 rukawa 这个不成熟的构想,将有订阅关系的数据节点都保存在一个集合中,数据节点可以订阅其他节点的值,并在订阅的节点发生valueChange时收到最新的values;
图画得有些抽象。
前言
其实很早就想做个尝试了,一直拖了很久,结果 灌篮高手,4月20就要上映了,再拖就没有脸去看流卡瓦暴打(???)泽北了,yep,rukawa 就是指流川枫。
希望它能像流川枫一样,虽然特立独行,但是还是能凭借强大的个人能力帮助整个项目。
使用
平平无奇的使用方式。
$ npm install rukawa
import { useRukawa } from 'rukawa';
const {
rukawaValues,
setRukawaValue
} = useRukawa({
name: 'test',
subscribes: [
'test-1'
],
initialValue: ''
})
提供一个 useRukawa 的 hook,可以从中导出 rukawaValues 和 setRukawaValue,
rukawaValues 是订阅的节点的 values 集合, setRukawaValue 是更改当前节点的值,
更改后,订阅该值的节点中的 rukawaValues, 会获取到最新的值。
example
一个简单的测试页面,点击操作会弹一个modal。
如果我把他们分为3个组件,[search ,table, modal],就必须要考虑数据怎么相互传递的问题,从 search 中怎么把 value 传递给 table 获取列表信息,然后 table 怎么把点击列的数据传递给 modal 进行展示等等。一般来说,就会用到 context 或者在父组件定义状态,俗称 状态提升。
就像前面说的, search 中的 value 明明用过一次以后就没用了,为啥我还要傻傻地给它保存起来呢,相同的逻辑也存在于 table 和 modal 中,这就是我认为的时效性较强的数据,这种数据的保存起来并没有什么意义,还不如直接通信,没有中间商赚差价。
(如果不想代码直接变一坨屎的话,建议日常开发中,多定义下常量,这里我把几个有订阅关系的组件name都定义了常量,也方便以后找哪些有订阅关系)
search component
export const Search = () => {
// 注册 node, 拿到 setRukawaValue
const { setRukawaValue } = useRukawa({
name: RUKAWA_SEARCH_CONST.SEARCH,
initialValue:''
})
const [val, setVal] = useState('');
return (
<div>
<Input
style={{ width: '200px', marginRight: '16px' }}
onChange={e => setVal(e.target.value)}
/>
<Button
type="primary"
onClick={() => {
setRukawaValue(val);
}}
>查询</Button>
</div>
)
}
table component
export const TableComponent = () => {
const {
rukawaValues,
setRukawaValue
} = useRukawa({
name: RUKAWA_SEARCH_CONST.TABLE,
// 注册节点,并订阅 search 中的值
subscribes: [
RUKAWA_SEARCH_CONST.SEARCH
],
initialValue: ''
})
const [data, setData] = useState<any[]>([]);
// 模拟 rukawaValues 变更重新获取列表数据
useEffect(() => {
const value = rukawaValues[RUKAWA_SEARCH_CONST.SEARCH];
if (value) {
setData([{
key: value,
name: value
}])
}
}, [rukawaValues])
const columns = [
{
title: '姓名',
dataIndex: 'name',
key: 'name',
},
{
title: '操作',
key: 'name',
render: (_: any, record: any) => (
// 修改自己的 node value,触发 modal 中 value 的更新
<a onClick={() => setRukawaValue(record.name)}>操作</a>
),
}
];
return (
<div style={{ marginTop: '32px' }}>
<Table bordered dataSource={data} columns={columns} />
</div>
)
}
search 中点击查询后, value 将直接被 table 获取,触发查询列表的操作,同理 table 中点击操作,会把 value 传递给 modal 触发展示,这里就不展示代码了。
结语
虽然有这个想法很久了,但是真正开始写其实也就没多久,所以功能上很不成熟,只能说基本实现了我最初的构想,而且这种直接订阅的数据流方案,可能并不会被认可,但是它确实能让我写代码时候避免那些令我无语的 状态提升 和 公共状态定义。
但是,就算不被认可,应该也会先努力迭代下去吧,自己的项目用一用应该死不了人。就像打海南时候,就算大家觉得流川枫独,不认可他的球风,哥们还是半场拿下29分,在赤木受伤的情况下扳平了比分~
后续迭代
尽量保证在4.20前能更新出一个令自己满意的版本,然后开开心心去看电影。
- 测试用例补一补
- 初始值的处理需要更加优雅一点,因为现在获取
value必须在节点创建以后,节点创建的时间先后问题,会导致拿不到设定的初始值 - 增加
options提供加强功能,比如防抖啥的。 - 可以便捷地查看当前的订阅关系