[Concent 速成] (1) 定义和共享模块状态 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
fantasticsoul
V2EX    React

[Concent 速成] (1) 定义和共享模块状态

  •  
  •   fantasticsoul 2020-09-19 19:23:05 +08:00 2189 次点击
    这是一个创建于 1899 天前的主题,其中的信息可能已经有所发展或是发生改变。

    pic

    开源不易,感谢你的支持, star concent^_^

    序言

    **[Concent 速成]**是一个帮助新手极速入门concent的系列文章,0 障碍地学习和理解 concent 状态管理思路。

    虽然学习和使用过reduxmbox之类的状态管理库,阅读此篇文章会更容易理解,但是没有使用过任何状态管理库的用户也能极速入门concent,真正的0 障碍学会使用它并接入到你的 react 应用里。

    注意上面强调了0 障碍,包括了学会使用和接入应用两个方面,为了达到此目的,api 要足够简单,简单到什么程度呢?简单到无以复加,简单到和react保持 100%一致,让新手无需理解额外的概览,以 react 组件的编写方式就能接入状态管理,但是呢也保留了更高级的抽象接口,让老手可以按照redux的模式去组织代码。

    来吧,展示!本期讲解的关键 api,包括一个 3 个顶层 apirunuseConcentregister,一个实例上下文 apisetState,学会使用这 4 个 api,你就已经会使用 concent 做为你的状态管理方案了。

    Hello world

    所有的框架都会以Hello world作为引导,我们此处也不例外,看看concent版本的Hello world是多么的简单。

    run 定义模块

    concent 和 redux 一样,有一个全局单一的状态树,是一个普通的 json 对象,不过第一层 key 规划为模块名,来帮助用户按照业务场景将状态切分为多个模块,便于分开管理。

    此处我们需要用到run接口启动 concent 并载入模块配置,配置一个名为hello的模块,并为其定义状态

    import { run } from 'concent'; run({ hello: { state: { greeting: 'Hello world' }, }, }); 

    register 注册类组件

    定义好了模块,我们的组件需要消费模块的状态,对于类组件,使用register即可

    import { register } from 'concent'; @register('hello') class HelloCls extends React.Component{ state = { greeting: '' }; changeGreeting = (e)=> this.setState({greeting: e.target.value}) render(){ return <input value={this.state.greeting} OnChange={this.changeGreeting} /> } } 

    上诉代码用register接口将HelloCls组件注册属于hello模块,concent 将向当前组件this上注入一个实例上下文ctx,用于读取数据和调用修改方法,同时也默默替换了 this 上的statesetState,方便用户可以 0 改动原组件的代码,仅使用register装饰一下类组件即可接入状态管理,这就是0 障碍学会使用并接入到 react 应用的基础,对于初学者来说,你会写 react 组件,就已经会使用了 concent,没有任何额外的学习成本。

    this.state === this.ctx.state; // true this.setState === this.ctx.setState; // true 

    上述代码里,还声明了一个类成员变量state等于 { greeting: '' },因为greeting和模块状态里的重名了,所以首次渲染之前它的值将被替换为模块里的Hello world,实际上这里可以不声明这个类成员变量state,写上它只是为了保证删除register装饰器这个组件也能正常工作,而不会得到一个undefinedgreeting初始值。

    useConcent 注册函数组件

    使用useConcent接口注册当前组件所属模块,useConcent会返回当前组件的实例上下文对象ctx,等同于上面类组件的this.ctx,我们只需要解构它取出statesetState即可。

    import { useConcent } from 'concent'; function HelloFn(){ const { state, setState } = useConcent('hello'); const changeGreeting = (e)=> setState({greeting: e.target.value}) return <input value={state.greeting} OnChange={changeGreeting} /> } 

    渲染组件

    最后我们看下完整的代码吧,我们发现顶层无Provider之类的组件包裹根组件,因为 concent 没有依赖React Context api实现状态管理,而是自己独立维护了一个独立的全局上下文,所以你在已有的项目里接入 concent 是非常容易的,即插即用,无需任何额外的改造。

    由于HelloClsHelloFn组件都属于hello模块,它们中的任意一个实例修改模块状态,concent 会将其存储到 store,并同步到其它同属于hello模块的实例上,状态共享就是这么简单。

    import ReactDOM from 'react-dom'; import { run } from 'concent'; import { register, useConcent } from 'concent'; run({/** 略 */}); @register('hello') class HelloCls extends React.Component{/** 略 */} function HelloFn(){/** 略 */} const App = ()=>( <div> <HelloCls /> <HelloFn /> </div> ); ReactDOM.render(App, document.getElementById('root')); 

    点我查看源码

    依赖收集

    无论是类组件还是函数组件,拿到state对象已被转换为一个Proxy代理对象,负责收集当前渲染的数据依赖,所以如果是有条件判断的读取状态,推荐采用延迟解构的写法,让每一次渲染都锁定最小的依赖列表,减少冗余渲染,获得更好的性能。

    function HelloFn(){ const { state, setState, syncBool } = useConcent({module:'hello', state:{show:true}}); const changeGreeting = (e)=> setState({greeting: e.target.value}); // 当 show 为 true 时,当前实例的依赖是['greeting'],其他任意地方修改了 greeting 值都会触发当前实例重渲染 // 当 show 为 false 时,当前实例无依赖,其他任意地方修改了 greeting 值不会影响当前实例重渲染 return ( <> {state.show?<input value={state.greeting} OnChange={changeGreeting} />:'no input'} <button OnClick={syncBool('show')}>toggle show</button> </> ); } 

    跨多个模块消费模块状态

    当组件需要消费多个模块的数据时,可使用connect参数来声明要连接的多个模块。

    使用 connect 参数连接多个模块

    如下面示例,连接 bar 和 baz 两个模块,通过ctx.connectedState获取目标模块状态:

    @register({connect:['bar', 'baz']}) class extends React.Component{ render(){ const { bar, baz } = this.ctx.connectedState; } } 

    connectedState拿到的模块状态依然存在着依赖收集行为,所以如果存在条件渲染语句,推荐延迟解构写法

    使用 setModuleState 修改状态

    通过调用实例上下文 apictx.setModuleState修改目标模块状态

    changeName = e=> this.ctx.setModuleState('bar', {name: e.target.value}) 

    结语

    此文仅仅演示了最最基础的 api 用法,帮助你快速上手 concent,如果你已经是老司机,特别是vue3 one piece已宣布正式发布的这个关头,如果你非常的不屑一顾这样笨拙的代码组织方式,暂先不要急着否定它,且打开官网看一下其他特性,一定有你喜欢的亮点,包括为 react 量身定制的composition api,模块级别的reducercomputedwatchlifecycle等等新特性,后面的速成会一一提到。

    Concent 携带一整套完整的方案,支持渐进式的开发 react 组件,即不干扰 react 本身的开发哲学和组件形态,同时也能够获得巨大的性能收益,这意味着我们可以至下而上的增量式的迭代,状态模块的划分,派生数据的管理,事件模型的分类,业务代码的分隔都可以逐步在开发过程勾勒和剥离出来,其过程是丝滑柔顺的,也允许我们至上而下统筹式的开发,一开始吧所有的领域模型和业务模块抽象的清清楚楚,同时在迭代过程中也能非常快速的灵活调整而影响整个项目架构.

    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5434 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 01:31 PVG 09:31 LAX 17:31 JFK 20:31
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86