React:正确设计State

        注:本文比较适用于有一定React+Redux项目经验的人员  

        当我们在使用Redux结合React使用的时候,Redux执行的过程中本质上任何一个时刻都是State的反应,State是Redux运行的枢纽,因此对于React+Redux结合运行的项目,设计好的State是非常重要的。接下来看,我们在设计state过程中常犯的两个错误:

    错误一:以API作为state设计的依据

                  以API作为设计state的依据往往是一个API对应全局state结构中的一部分,且这部分结构同API返回的数据结构保持一致(或者接近一致),以博客项目为例,获取帖子列表的API返回的数据结构如下:

                [

             {id:1,title:"相爱就一起吧",vode:8,updatedAt:"2012-4-5",author:{id:"23disdas",username:"小王"},

             {id:2,title:"相爱就一起吧",vode:9,updatedAt:"2012-4-5",author:{id:"23disdas",username:"小李"},

             {id:3,title:"相爱就一起吧",vode:10,updatedAt:"2012-4-4",author:{id:"23disdas",username:"小青"},

            {id:4,title:"相爱就一起吧",vode:12,updatedAt:"2012-4-8",author:{id:"23disdas",username:"老贾"}

               ]

             当我们在查看帖子详情时,需要调用帖子详情API和获取帖子评论的数据API,这两个结构返回的数据结构分别如下:

            //帖子详情

            {id:4,title:"相爱就一起吧",vode:12,updatedAt:"2012-4-8",author:{id:"23disdas",username:"老贾",

            content: 前端UI的复杂化,本质问题是如何将源于服务器的动态数据和用户交互行为高效的反应到复杂的用户界面上。React另辟蹊径,通过引入虚拟DOM,状态,单项数据流等设计理念形成以组件为核心,用组件搭建UI的开发模式理顺了UI开发过程中,完美的将数据,组件和状态,UI映射到一起,极大的提高了开发大型web应用的效率

             }

      //评论列表API返回的数据结构

       [

        {id:3,title:"相爱就一起吧",content:"React效率真高",updatedAt:"2012-4-4",author:{id:"23disdas",username:"小青"},

     {id:4,title:"相爱就一起吧",content:"React+Redux真是攻守兼备",updatedAt:"2012-4-4",author:{id:"23disdas",username:"小青"},

  .........

     ]

 以上三个接口分别作为state的一部分,组合在一起就构成了应用全局的state:

[

  posts:[

 {id:3,title:"相爱就一起吧",vode:10,updatedAt:"2012-4-4",author:{id:"23disdas",username:"小青"},

 {id:4,title:"相爱就一起吧",vode:12,updatedAt:"2012-4-8",author:{id:"23disdas",username:"老贾"}

........

],

currentPost:{

 {id:4,title:"相爱就一起吧",vode:12,updatedAt:"2012-4-8",author:{id:"23disdas",username:"老贾",

  content: 前端UI的复杂化,本质问题是如何将源于服务器的动态数据和用户交互行为高效的反应到复杂的用户界面上。React另辟蹊径,通过引入虚拟DOM,状态,单项数据流等设计理念形成以组件为核心,用组件搭建UI的开发模式理顺了UI开发过程中,完美的将数据,组件和状态,UI映射到一起,极大的提高了开发大型web应用的效率}

},

currentComments:[

 {id:3,title:"相爱就一起吧",content:"React效率真高",updatedAt:"2012-4-4",author:{id:"23disdas",username:"小青"},

    {id:4,title:"相爱就一起吧",content:"React+Redux真是攻守兼备",updatedAt:"2012-4-4",author:{id:"24disdas",username:"老贾"},

  .........

]

]

这个state中,post和currentPost存在很多重复的信息,而且posts,currenComments是数组类型的结构,不便于查找,每次查找某条记录时,都需要遍历整个数组,这些本质上都是因为API是基于服务器端的逻辑设计的,比如,虽然获取帖子列表时已经获得了帖子标题,作者等基本信息,但对于获取帖子详情API来说,根据API的设计原则,这个API依然应该包括这些基本信息,而不能只是返回帖子的正文内容,再比如,posts,currentComments之所以返回数组结构,是考虑数据的有序性,分页等因素。

错误二:以页面UI为设计state的依据

         既然不能依据API设计state,那可不可以基于UI来设计state呢,基于UI的state,页面UI需要什么样的的数据结构,state就设计成什么样子,以我们熟悉的todos为列,页面会有三种状态:显示所有事项,显示所以已办事项和显示所有代办事项,以页面UI为state的设计依据

{

all:[

 {id:1,text:"todo1",completed:false,},

 {id:2,text:"todo2",completed:true,}

],

uncompleted:[

{id:1,text:"todo1",completed:false,},

],

completed:[

 {id:2,text:"todo2",completed:true,}

]

}

这个state的设计对于展示UI的组件来说使用起来非常方便但当前应用属于那种状态,就用对应状态的数组类型数据渲染UI,不用做任何数据转换,但这种state设计存在的问题也很容易本发现,一种是state依然存在数据重复的问题,二是当新增或者修改一条记录时,需要修正不止一个地方。例如,当我们新增一条记录时,all,uncompleted这两个数组都要添加到这条新纪录,这样设计state既会造成存储浪费,又会存在数据不一致的风险。

 总结:这两个state的设计方式实际上是设计state时的两种极端做法,在实际项目中,完全按照这两种方式设计state的开发者并不多,但是我们大多数人都会受到这两种方式的影响。

 

 二,合理设计state

   知道了state的错误设计方式,下面我们来看下应该如何设计合理的state,设计state时,最重要的是记住一句话:像设计数据库一样设计state。把state看着是一个数据库。state中的每一部分都看着数据库中的一张表,状态中的每一个字段对应表的一个字段,设计一个数据库应该遵循以下三个原则:

(1)数据按照领域分类存储在不同的表中,不同的表中存储的数据不重复。

 (2)表中每一列的数据读依赖于这张表的主键。

 (3)除了主键之外,其他相互之间不能有直接关系。

   根据这上原则可以对应state的设计原则:

 (1)把整个状态按照领域分成若干个子状态,子状态之间不能保存重复数据。

   (2)state以键值对的结构存储,以记录的key或者id作为记录的索引,记录中的其他字段都依赖于索引。

 (3)state中不能保存可以通过state中以有的字段计算二来的数据,既state中的字段不能相互依赖。

   按照这三个原则重新设计博客的state,按领域划分,state可以拆分为state:posts,comments,users,posts中记录以帖子id为key值,结构如下

 posts:{

    3:{id:3,title:"相爱就一起吧",vode:10,updatedAt:"2012-4-4",author:{id:"23disdas"},

    4:{id:4,title:"相爱就一起吧",vode:10,updatedAt:"2012-4-4",author:{id:"24disdas"},

}

这个结构相比以前的按API划分的state结构的变化之处主要有两点:第一点是,posts中的数据类型由数组类型改为以帖子id为key的JSON数据类型;第二点是,author不在存储完整的作者信息,只存储作者的id,第一个变化方便在使用posts时快速的根据id获取对应的帖子数据;第二个变化时把原本嵌套的数据结构扁平化,避免在查询和修改的嵌套数据时需要向下访问多个层级的繁琐同时扁平化的数据结构更利于扩展。

但这个state还有不满足应用需要的地方:键值对的存储方式无法保存数据的有序性,但对于帖子列表,有序性显然是需要。解决这个问题可以通过定义一个数组的属性allIds存储帖子的id,同时将之前的键值对的数据类型存储在byId属性下:

posts:{

byId:{

    3:{id:3,title:"相爱就一起吧",vode:10,updatedAt:"2012-4-4",author:{id:"23disdas"},

    4:{id:4,title:"相爱就一起吧",vode:10,updatedAt:"2012-4-4",author:{id:"24disdas"},

  ......

},

allIds:[3,4,....]

},

这样一来,allIds负责维护数据的有序性,byId负责根据id快速查询对应的数据,这种设计state的方式是很常用的一种方式。

posts不能保存完整的作者信息,那么作者信息查询就依赖领域的users对应的state,应用中不关注作者的顺序,因此我们只需要使用作者id为key的键值对存储数据即可:

users:{

23disdas:{id:23disdas,username:"小青"}

24disdas:{id:24disdas,username:"老贾"}

}

评论数据通过单独的API获取的,但评论数据从属与某个帖子,这个关系该如何在state中体现呢?有两种方法:第一种是在posts对应的state中增加一个comments属性,存储该帖子对应的评论数据id,第二种是在comments对应的state中增加一个byPost属性,存储以帖子id作为key,以这个帖子下的所有评论id作为值得对象。使用第二种方法,当调用API请求评论数据时,只需要修改comments对应的state即可,使用第一种方法还需要修改posts对应的state,因此这里使用第二种方法

comments:{

byPost:{

3:[3,4,5],

........

},

byId:{

 {id:3,title:"相爱就一起吧",content:"React效率真高",updatedAt:"2012-4-4",author:{id:"23disdas",username:"小青"},

 {id:4,title:"相爱就一起吧",content:"React效率真高",updatedAt:"2012-4-4",author:{id:"23disdas",username:"小刘"},

 {id:5,title:"相爱就一起吧",content:"React效率真高",updatedAt:"2012-4-4",author:{id:"23disdas",username:"小李"},

}

}

byPost保存的帖子的映射关系,byId保存评论的id到评论数据的映射关系。由post,comments和users三个领域组成的state结构如下:

{

posts:{

byId:{

....

},

allIds:[....]

},

comments:{

 byPost:{

  byPost:{

  ....

},

byId:{

...

}

},

users:{

....

}

}

}

目前为止,我们的state都是根据领域数据进行设计的,但实际上,应用state不仅包含领域数据,还包含应用状态数据和UI状态数据。应用状态数据指反应应用行为的数据,列如,当前的登录的状态,是否有API请求在进行等。UI状态数据是代表UI当前如何显示的数据,列如对话框当前是否处于打开状态等。

注:现在我们不分开发者还是喜欢把UI状态数据保存在组件自己的state中,由自己管理,而不是交给redux管理,这也是可选的做法,但将UI状态数据也交给Redux统一管理也有利于应用UI状态的追溯。

  在博客项目中,我们将应用状态分为两部分,一部分专门记录登录认证相关的状态,保存到子state auth中,其余保存到state app中,这两部分state结构如下:

 app:{

   requestQuantity:0,//当前应用中进行的API请求数

   error:null,//应用全聚德错误信息

},

auth:{

userId:null,

username:null

}

app中保存了当前进行应用的API请求数量和应用错误信息,auth中保存了当前登录的用户ID和用户名。当需要管理应用中应用状态数据增多时,可以进一步将app拆分成多个state,类似我们将UI状态数据保存到子state ui中:

ui:{

addDialogOpen:false,//用于新增帖子对话框的显示状态

editDialogOpen:false//用户编辑帖子对话框的显示状态

}

当然这里涉及的ui状态数据比较少,只保存了新增帖子对话框和编辑帖子对话框的状态,至此,由领域数据,应用状态数据,UI状态数据组成的完整的state结构如下:

{

posts:{

byId:{

....

},

allIds:[....]

},

comments:{

 byPost:{

  byPost:{

  ....

},

byId:{

...

}

},

users:{

....

},

app:{

......

},

auth:{

.......

},

ui:{

......

}

}

}

本篇完,在下一篇中我们将来谈谈在React+Redux项目中如何设计模块(Module)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值