资讯专栏INFORMATION COLUMN

dingDang ( 一个管理react数据层的产品 )

Pines_Cheng / 907人阅读

摘要:一个管理数据层的产品地址我们将定义为一个产品而不是一个框架亦或工具包。认识装饰器,一切故事的开始,目前仅仅只有一个参数。我们对场景的定义多个页面之间针对同一组业务数据进行的一连串行为动作来完成的一组业务,我们称之为场景。

dingDang ( 一个管理react数据层的产品 )

github地址

我们将dingDang定义为一个产品而不是一个框架亦或工具包 。

因为dingDang在设计之初就将用户的开发体验放在第一位,而性能问题则放在第二位。

当然这并不意味着我们就是一个性能很糟糕的产品,只是说我们会在性能能够接受的范围内尽可能的去让开发者的开发体验更好。 

事实上dingDang的内部数据全部都采用了immutable数据类型。

dingDang中使用了大量的 Promise 于 immutableJS , 如果你对此不太熟悉,可以先熟悉一下 这两个知识点。
认识 DingDang
DingDang装饰器 , 一切故事的开始 , 目前 仅仅只有 sceneStorage 一个参数 。

主要用来定义场景的临时数据存储方案。

比如说:

在react-web 中 你应该采用sessionStorage 或者 localStorage  , 

而在react-native 中,你应该采用AsyncStorage 。

在sceneStorage中的3个函数 所有的返回值 要求是标准的 JS Object , set 方法的 key 我们也会传输一个标准的 JS Object

@DingDang({
    sceneStorage: {
        set: (k, v) => sessionStorage.setItem(k, JSON.stringify(v)),
        get: (k) => JSON.parse(sessionStorage.getItem(k)),
        del: (k) => sessionStorage.removeItem(k)
    }
})
class App extends React.Component {

    render() {
        return (
            
                
                
            
        )

    }

}

render(
    ,
    document.querySelector("#app")
)
认识一个完整的Store
const store = {
    namespace: "demoStore",
    state: {},
    rules: {},
    reducer: {},
    effect: {},
    initialization: () => false,
    destroy: () => false
}
事实上,store中只有namespace字段是必须的,其他的字段如果你不需要的话,可以不写。
namespace(命名空间)
该属性的值是一个字符串 , 在dingDang中请务必保证namespace的唯一性,因为我们需要以此为依据去判定用户是否进入了一个全新的场景。
state (状态机)
该属性中主要用来存储store中的所有数据集 , 一般来说dingDang会将这里的数据都转换为immutable类型。
rules (规则)
首先让我们来看一个完整的rules的案例:
export default {
    namespace: "demoStore",
    state: {
        name: "张三"
    },
    rules: {
        name: [
            {
                validate: (state, name) => name ? true : false,
                error: "用户姓名不能为空",
                miss:false
            },
            {
                validate: (state, name) => name.length < 6,
                error: "用户姓名最大长度不能超过6!",
                miss:true
            }
        ]
    }
}

rules 中配置的其实是针对state 中数据的校验机制 ,比如在我们的state 中有name这个字段 ,
那么你可以在 rules中也配置一个 对应的name字段 , 但是在rules中 , 该字段的值必须为数组, 
具体的校验规则,我们会按照数据的先后顺序来执行。
数据中存储的是每一个 独立的 rule ,一个标准的 rule如下:
{
    validate: (state, name) => name ? true : false,
    error: "用户姓名不能为空",
    miss:false
}
validate : 值为一个判断函数 , 该函数我们会自动的注入全量的state参数, 以及你即将赋予字段的新值 , 
例如:name 。 这里的校验逻辑为: 如果满足你所需的格式 ,那么返回true , 否则false

error: 错误提示 , 在执行validate得到false之后抛出的异常信息

miss : 定义是否丢失, 这里的含义是指, 如果即将赋予的新值不能满足校验条件, 那么新的值是否丢失掉(即不存储进state)
如不定义该字段, 那么该字段默认为false , 即不丢失 ,依然赋予state。
reducer (折叠器)
该字段内存储的应该都是纯函数 ,在这里你可以直接去针对state赋予全新的值 , 首先让我们来看一段相对完整的reducer代码案例:
export default {
    namespace: "orderStore",
    state: {
        goodsList: [
            {
                id: 1,
                name: "方便面",
                price: 2
            },
            {
                id: 2,
                name: "笔记本电脑",
                price: 3281
            }
        ],
        cart: []
    }
    reducer: {
        addToCart: (state, goods) => {
            return state.set("cart", state.get("cart").push(goods));
        }
    }
}
正如上面的demo所示 , 我们会为reducer 中的每一个函数的 第一个参数都注入一个全量的 immutable 类型的state ,
而第二个参数则是你在调用的时候传入的。我们对reducer的要求是,最后你必须返回一个你期望的、全新的、全量的state。

下面我们看一段调用reducer 的案例代码:

@Page(Store)
export default class GoodsList extends React.Component {

    static injectProps = {
        goodsList: [],
        cart: []
    }

    _addToCart = async (n) => {
        const {execReducer} = this.props;
        try {
            await execReducer("addToCart")(n)
        } catch (err) {
            console.log(err)
        }
    }

    render() {

        const {injectProps: {goodsList}} = this.props;

        return (

            
                
                    
                        
                            
                              this._addToCart(n)
                                }>加入购物车
                            }/>
                        
) } }
上述的demo中 , 我们希望你不要被过多的去关注可能对于你来说未知的代码影响,你只需要关注我们希望阐述的内容即可(即_addToCart函数),
首先我们应该明确_addToCart函数是一段调用reducer的代码 , 其次我们观察到 , _addToCart函数采用了async await 的方式来书写,
说明该函数是异步的 , 并且返回了一个Promise对象给我们 。

事实上,在你执行reducer时,dingDang内部有可能会去rules中取到对应的规则进行校验,如果出现异常的话会reject出来给你 , 所以这里
catch到的err其实是你的rules中所配置的信息 , 同时如果你的reducer没有任何毛病, dingDang依然会有返回值给你,这个返回值是reducer
之后的一个全新的state。

effect (副作用的)
我们将这里定义为副作用的 , 也就是希望你将所有的非纯函数在这里定义(依赖于外部因素的函数,相同输入并非能保障得到相同的结果 ,比如调用API)。
惯例,让我们看一段完整的effect案例:
export default {
    namespace: "orderStore",
    state: {
        goodsList: [],
        cart: []
    }
    reducer: {
        addToCart: (state, goods) => {
            return state.set("cart", state.get("cart").push(goods));
        }
    },
    effect:{
        queryGoodsList: async ({resolve , reject , state , execReducer , onChangeState} , params) => {
            const result = await queryGoodsListByAPI(params);
            if( result.success ){
                await onChangeState(result.goodsList).catch(err => reject(err));
                resolve(true)
            }else {
                reject( result.error )
            }
        }
    }
}
effect 的参数,我们注入的比较多, resolve 、 reject 这里就不作解释了 , 如果你不是很清楚, 建议去温习一下Promise 。state 也不作过多的解释了。
这里我们稍微来提一下 execReducer 和 onChangeState 这两个函数 。 execReducer 在前面我们也见过 , 这个函数属于dingDang的系统级函数,主要用来
辅助你去调用reducer。  onChangeState 也是一个dingDang系统级函数 , 这个函数是便于你直接去修改state中的数据 。
我们注意区分一下reducer 和 onChangeState 的区别 , 一般来说reducer的调用方式如下:
    execReducer("addToCart")(params);
而onChangeState的调用方式
    onChangeState(jsObjectParams)
我们总结概括一下,reducer其实是你用来去调用你自己声明的指定的一个reducer函数 , 而onChangeState 其实是直接去修改的state中的值 。 一般来说,
如果你无需做业务逻辑, 仅仅只是想把一个数据放进state , 我们推荐你使用 onChangeState 的方式, 请不要担心rules 的校验问题 onChangeState 依然
会去执行rules的校验代码 , 而如果您不仅仅是将一个数据放进state , 在这中间可能还有很多业务逻辑代码, 那么我们推荐你自己去写一个reducer , 然后使用
execReducer 来进行调用

initialization (store生命周期中的初始化)
initialization 是一个初始化函数 , 这里不想过多的说什么 , 主要重点说一下 , 我们为这个函数注入了
execReducer 、 execEffect 、 onChangeState
destroy (store生命周期中的销毁)
与initialization同理 , 注入的参数也相同

系统级函数介绍 execReducer
高阶函数 , 主要用来调用 reducer  , 返回Promise
execEffect
高阶函数 , 主要用来调用 effect  , 返回Promise
onChangeState
简化修改state中值的方式

Page 装饰器
用来装饰一个页面的入口 ,装饰之后的页面 可以使用声明式注入 demo案例如下

@Page(store)
class Home extends React.Component {


    static injectProps = {
        initName: null,
        name: null
    }

    render() {

        const {injectProps: {initName, name}, onChangeState} = this.props;

        return (
            

{initName}

{name}

) } }
Component装饰器
用来装饰一个页面下的子组件,装饰之后的页面 可以使用声明式注入 demo案例如下

@Component
class Card extends React.Component {

    constructor(props) {
        super(props)
        this.state = {
            error: ""
        }
    }

    static injectProps = {
        name: null
    }

    render() {

        const {injectProps: {name}} = this.props;

        return (
            
{name}{this.state.error}
) } _onChangeName = () => { const {onChangeState} = this.props; onChangeState({name: "李四李四李四李四李四李四"}).catch(e => this.setState({error: e})) } }
injectProps (声明式注入)
在组件中使用 static injectProps 的方式可以来声明 让dingDang 从store中为你注入哪些数据。

ScenePage (场景页面)
在某些业务背景下,我们可能需要多个页面来完整一组业务。

假设我们需要 A B C 3个页面来完成一组业务 , 在这组业务中 数据其实基本上是相同的, 针对数据的某些业务操作也是相同的。

在这种背景下,我们提出了场景的概念。

我们对场景的定义:多个页面之间针对同一组业务数据进行的一连串行为动作来完成的一组业务,我们称之为场景。

普通的Page装饰器装饰之后的页面只能取自己独立的那份store , 而使用ScenePage装饰之后的多个页面 , 只要绑定的Store
的namespace相同,那么这个Store的声明周期将一直存在,直到用户跳出当前场景。

使用
npm install 0.1.1-alpha --save

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/83636.html

相关文章

  • 我是如何使用React+Redux构建大型应用的

    摘要:所以前端工程师不仅仅是写好页面和做好兼容性。对前端工程师的技术能力也会带来考验。这里想要说的是,如果使用了,使用了服务端渲染,对于前端工程师的个人素质要求会比较高,因为需要处理很多服务端的问题。 背景 我们团队有个项目由于开发时间较长,且是前后端杂糅的开发方式,维护成本很高,在线上暴露的问题很多。而且因为对接了公司一百多条产品线,每天都会接到大量的客诉和产品线反馈的问题。2017年11...

    canopus4u 评论0 收藏0
  • 学习React系列2-[解读]Thinking in React

    摘要:扩展单一职责原则又称单一功能原则,面向对象五个基本原则之一。马丁表示此原则是基于汤姆狄马克和的著作中的内聚性原则发展出的。 [解读]Thinking in React 原文:http://facebook.github.io/react/docs/thinking-in-react.html 前言 Thought is the seed of action 这是放置在官方的QUICK ...

    tomorrowwu 评论0 收藏0
  • 漫谈 React 组件库开发(一):多层嵌套弹层组件

    摘要:引言组件中有很多弹出式组件,常见的如,以及等。这样一种层次结构在实践中大大降低了各类弹层组件的实现和维护成本。但是的组件实现了一个大多数组件库都没有实现的功能弹层的嵌套处理。 引言 UI 组件中有很多弹出式组件,常见的如 Dialog,Tooltip 以及 Select 等。这些组件都有一个特点,它们的弹出层通常不是渲染在当前的 DOM 树中,而是直接插入在 body (或者其它类似的...

    warmcheng 评论0 收藏0
  • 前端每周清单第 56 期: D3 5.0,深入 React 事件系统,SketchCode 界面生成

    摘要:雅虎从很早就开始招聘和培养研究型人才,雅虎研究院就是在这个过程中应运而生的。今天我就来说一说雅虎研究院的历史,以及过去十多年间取得的成就,聊一聊如何通过引进高级人才,迅速构建起一支世界级的研发团队。 showImg(https://segmentfault.com/img/remote/1460000013995512); 作者:王下邀月熊 编辑:徐川 前端每周清单专注大前端领域内容,...

    lavnFan 评论0 收藏0
  • 那些年的体验技术部

    摘要:随着业务的爆发,团队人数迅速增长起来,团队名也从前端开发部改名成体验技术部,意在体现前端工程师的核心竞争力用技术解决产品体验问题。前后端分离的研发模式在社区流行起来,体验技术部最先实践的是基于的应用层方案。2008 年对中国人是复杂的一年,冰灾,大地震,奥运会接踵而至。对玉伯来说也一样,赶在奥运会排查临时人口之前,玉伯从北京中科院软件所离开,凭着自己几年来在程序开发上的经历和对新兴前端行业的...

    sean 评论0 收藏0

发表评论

0条评论

Pines_Cheng

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<