产品的反馈系统设计

什么是反馈系统? 

  反馈系统(feedback system)是具备闭环信息通道的系统。前端

  定义: 将系统的后果或输出信息采集、处理,而后送回输入端并据此调整系统行为的系统。因为信息流通构成闭合环路, 它也称为闭环系统。 反馈做用经常用于检测信号误差以及对象特性的变化,并以此来控制系统行为以及消除偏差。 它又被称为反馈控制或者按照偏差控制的系统。node

 

为何须要反馈系统?

  1. 用户的须要。用户在使用过程当中会遇到问题,对于此问题他但愿可以有渠道去反馈,主要目的有三。
    1. 其一: 提出对于产品的一些我的意见,帮助开发者更好地改进产品。
    2. 其二: 在使用过程当中可能遇到产品的某些bug,须要渠道来宣泄本身的情绪。
    3. 其三: 在使用过程当中遇到了某些问题,如找不到入口、登陆出错等等,用户须要得到及时的解答。 
  2. 产品开发人员的须要。开发人员尤为是创业团队是更加须要反馈系统的,主要目的有二。
    1. 其一: 及时了解产品的问题与不足并改进产品,以此来不断地优化产品。
    2. 其二: 对于用户的问题做出及时的解答,提高产品的用户体验。 

 

反馈系统的类型有哪些?

  主要能够分为自建的反馈系统和使用第三方工具做为反馈系统:react

  1. 自建的反馈系统。
    1. 邮件形式 --- 即用户将反馈信息提交以后经过邮件的形式发送到开发人员,而后开发人员进行邮件形式的回复。 
    2. 对话形式 --- 即用户的反馈会直接显示在页面上, 开发人员的回复、用户的追问等以对话的形式显示出来。
    3. 工单形式 --- 即用户的反馈会以工单的形式显示, 其处理的过程包括处理中、处理完毕等具体流程会显示出来。
    4. 需求池形式 --- 即用户的反馈会所有显示出来。 需求池就是对全部需求的集合,可是开发人员须要及时地对需求池进行优化。
  2. 第三方工具做为反馈系统。
    1. QQ
    2. 微信
    3. 官方微博
    4. 微信公众号

 

具备反馈系统的网站推荐

邮件形式:webpack

  

  如Tower,咱们在 https://tower.im/help 的底部能够看到下面的界面:es6

  进入帮助页面,若是咱们没有找到答案,就能够在底部发现这样的一个反馈系统,邮箱是网站默认填充的, 而后咱们能够在内容框中填写文字、图片等, 开发、客服人员收到邮件以后会经过邮件的方式发送给用户。 web

  显然,这种方式是封闭的,只有发问的用户能够收到解决邮件,而其余用户看不到更多用户的问题与回答。 mongodb

  几分钟以后,我就接受到了这样的一封反馈邮件,很好地解决了个人问题, 能够看到,Tower的入口也是很是方便地。typescript

  

 

 

  

对话形式:数据库

  

  叮当网站就是这种对话形式的反馈,在右下角有一个留言的图片,点击这个图片以后就会进入一个对话的页面,这个页面还可使用一个新的iframe来显示,以下所示:npm

      

  这种方式最大的好处就是直观、方便追问、具备及时性, 可是不太方便跟踪员工处理的进度状况。 

 

 

 

工单形式:

  

   在阿里云的网站上,咱们能够添加建议并提交,提交以后咱们能够看到显示以下:

  能够看到提交以后,就会造成一个工单,实时的反映当前状况, 即 ‘已提交’、‘预审经过’、‘已采纳’、‘已实现’ 几个步骤。 对于仅仅是提交的建议,咱们还能够进行再次编辑,另外,全部的建议其余用户也都是能够看到的,全部人(固然包括管理员)能够进行反馈、评论、投票等功能。 

  在此网站能够查看:https://connect.aliyun.com/suggestion/5293?spm=5176.8409797.user.1.38c60aa8v2lqt7

  

 

 

 

需求池形式:

  

  

  https://www.mockplus.cn/

  这是一个作原型的工具,更为高效、简单。

 

 

反馈系统基本框架

   

   从反馈系统的基本框架来看,此系统在在前端至少须要两个页面(或者说是网站) --- 用户反馈网站 以及 后台管理网站。

前端用户反馈页面

第一部分:

  即须要在网站的的主页面或者帮助页面处提供一个入口,由此进入反馈界面,在反馈界面中的须要提供一个表单用于提交反馈,内容以下:

  • 表单的字段里须要包括用户的userName等基本信息用做后台记录。
  • 选择提交反馈的类型
    • 通常问题。如界面UI问题、网站进不了、没法登录等等问题。 
    • 产品问题。须要具体制定某个产品,即针对某个产品进行的反馈。若是这个反馈插件只是放在了某一个特定产品的网站上,那么咱们就不须要选择产品了,若是反馈插件放在了一个公司(这个公司下有不少产品)网站上,咱们就须要让用户选择特定的产品。
  • 输入反馈的具体内容。 能够输入文字,也能够输入图片。 

  输入完成以后提示已提交至后台便可。 

 

  另外,在反馈界面,咱们还能够列出一些常见的问题和回答,这样,用户也许就不须要去提问了。

 

 

第二部分:

  固然,除了一个提交反馈的表单仍是不够的,还须要一个“个人反馈”界面。

  即在第一部分中,咱们反馈进行了提交,在“个人反馈”界面就须要展现对于个人全部反馈的详情了。 

  • 这个反馈的详情是后台管理员进行回复的。
  • 用户能够在管理员回复的基础上进行追问 。

     既然有个人反馈界面,这也就是说每次你进入这个网站的时候仍是须要惟一标识的,那么就须要使用登陆和注册功能,这样才能在你下一次登陆的时候将你的相关的建议展现出来。 

 

 

前端后台管理界面

  后台管理界面会稍微复杂一些。 

  • 能够查看全部产品列表。
  • 能够建立新的产品。
  • 能够查看某产品的反馈列表。
  • 能够关注产品。 由于每个使用后台管理的人多是不一样的,有人是为了关注 maxhub 相关产品,有人是为了关注 seewo 相关的产品。关注以后,当有新的消息时,就会给出后天相应的提示。 没有关注的也是能够看到反馈列表的,只是不会给出提醒。
  • 能够查看某个反馈的详情。
  • 能够回复单个反馈(采用堆楼模式)。
  • 能够操做单个反馈,实现状态的改变(新建、已回复(回复后自动确认)、已关闭(关闭后自动修改))。
    • 新建 --- 即一个新的反馈,须要及时的回复。 
    • 已回复 --- 即对于新的反馈,已经给予了回复。
    • 已关闭 --- 即咱们看到已经回复完成以后,对方也满意了,就能够关闭这个反馈了,那么咱们就不须要再去常常查看了 。 那么此时应该通知客户端已经关闭了反馈、而且不可再回复了吗? 实际上也是能够的,既然已经解决了问题,就能够关闭了。
  • 条件搜索。 根据某个条件,搜索到相应的用户问题。

 

重要: 能够作成推送平台的js插件!

  • 在任何地方引入以后,能够在页面上添加入口。
  • 点击入口以后展现一个简单的反馈界面,能够提交反馈。
  • 考虑js插件的可配置性(入口位置、颜色、文案等等)。
  • js插件支持移动端。 

 

作成插件以后,咱们就能够在多个产品的主页上使用了。 这种场景每每是用于一家较大的公司,公司旗下有不少的产品,每一个产品可能都会单独建站,在每个网站上,咱们能够添加一个特定产品的js插件,然后台管理页面是保持基本不变的。 

 

 

 

 

后台接口

  登陆、注册、请求反馈界面、请求后台管理界面、添加产品、请求全部产品、请求某个产品下的全部反馈(每条反馈是一个文档,每一个文档中包含了反馈的具体信息,题目,信息,评论(评论须要包括评论人、评论时间等),状态、所属用户等等,键值对不是简单的string,设计的有规律一些便可。), 请求某个用户的全部建议(在某个产品下去查找便可,那么保存用户信息时还须要保存用户所使用到的产品类别,这样在搜索产品时会比较快), 

 

  

最终的项目架构

这个项目分红了两个文件来作,一个做为主服务器,另外一个做为辅助服务器,那个辅助服务器在发送请求时是代理到主要的服务器上的。前端采用基本的分层方式,后端采用的是MVC的架构方式。 

build是服务器相关文件。 model是数据库的相关文件。 node_modules是存放的一些包。 router是存放的路由文件。 src是react的相关文件(src中components存放一些基本的组件,pages存放整个的页面, redux存放的是项目状态管理的相关文件,index.tsx是react项目的渲染页面)。 www是服务器上存放的静态文件。 最后就是基本的 package.json、settings.js(数据库配置)、tsconfig.json(typescript的相关配置文件)、webpack.config.js(webpack相关文件)。 

 

 

 

 

遇到的问题

一、应该创建几个文件,即开启几个服务器(项目)? 

第一种方式: 开启一个服务器。

  即认为只有一个服务器,在用户点击按钮时,触发一个请求,从后端请求到反馈系统的页面,这样就能够进行简单的操做了。 

  对于管理员,也能够请求到服务器端的一个管理员的页面。 

  这样在技术上是能够作到的,可是在前端处理上会出现问题。 好比使用webpack打包的时候,就会把全部的js打包,可是实际上在用户和管理员处所使用的js并非全部的,这样就会形成浪费。 

  

第二种方式: 开启两个服务器

  这样的方式最简单、清楚、明了。 

 

结果: 实际上,咱们应该当作两个项目来作,也就是说,须要同时进行两个项目来作,一个项目用于写后天管理系统(较多主要是数据库的处理),另外一个项目主要是写前端反馈系统。 

 

二、 这个任务的实际使用场景是这样的吗?

  即管理员的界面始终只有一个,而用户反馈界面的界面是会随着不一样产品的改变而改变的,会有不一样的网站来进行请求。 

 

 结果

  • 反馈平台是比较核心的,而且这个能够看作是固定的,由管理员来使用,而且反馈平台管理员端的后台代码也是比较比较重要的,经过其为反馈系统提供API。 

 

三、 使用react、 ant.design能够吗? 

 结果: 能够尝试使用 ant.design , 由于 ant.design 是蚂蚁的一套前端框架,适合react项目的使用,可能会遇到一些坑,可是仍是能够尝试考虑使用的。

 

 

四、 这里所说的插件究竟是什么?怎么去理解? 可配置是说添加一个配置文件、配置对象吗? 

   插件实际上就是一段js代码,经过这个js代码,咱们能够将这个js插件暴露出来的方法进行 init 而后适用在某个产品上。 

 

 

五、什么是堆楼模式? 这里所使用的反馈系统是工单形式的吗?

   即评论是一层一层的,而不是缩进的形式。 反馈系统能够看做是工单形式的。  

 

 

六、总体的思路应该是怎样的? 

  对于后台管理界面是直接使用node做为后台,而后使用react来写前台管理界面,这个是通用的。 另外,这个后台除了提供管理界面以外,也应该提供反馈平台所须要的接口。 另外,还须要一个通用的普通端,这里能够本身选产品的类型等。 

 

 

七、数据库的设计应该是怎么样的?

   在数据库中,咱们须要存储的信息包括管理员信息、用户信息、建议信息、评论信息、产品信息等等, 对于这些信息,咱们应当尽可能采用扁平化的风格进行存储。使用链接的方式。 

  (1)产品存储

  • 产品名称 (name) --- 用于在用户获取到以后在前端显示出来。
  • 产品ID (productId) --- 用于惟一的标识产品。 
  • 产品被建立的时间 (savedTime) --- 存储了时间以后,在显示产品时咱们就能够采用产品建立时间的前后顺序来获取,这样就是有序的了。
  • 关注该产品的人(relatedPersonId) --- 即若是某用户关注了该产品, 咱们就将这我的的ID添加进来,这样用户在获取 我关注的产品 时,就能够经过查询数据库查询到相应的产品。 而且做为管理员,应该不是不少,因此说将关注该产品的管理员加入也是能够的。
  • 建立该产品的人 (createdPersonId)--- 只有建立了这个产品的人才可以删除这个产品。

  (2)建议存储

  • 建议的ID(suggestionId)--- 惟一的标识一条建议。
  • 提建议的人(person) --- 用于在展现产品时展现出提建议的人。
  • 提建议的人的Id --- 用于建立该建议的用户进行查找。
  • 建议建立的时间 (suggestionTime) --- 用于在获取产品时,针对时间进行排序;展现。
  • 相关的产品ID(productId) --- 在列举产品的建议时,能够根据建议中的这个字段获取到全部的相关建议。
  • 建议的类型(type) --- 在列举某个产品的建议时,咱们能够针对功能建议、产品缺陷、产品需求三个方面来分别展现相关的建议。
  • 建议内容(content)--- 这个固然是不可少的,由于咱们须要展现出来。
  • 建议状态(status)--- 用户发出一个建议以后,须要管理员来回复处理,好比已提交、被拒绝、已接纳、实现建议的内容等等。 

  (3)评论存储

  • 此条评论所属的建议的ID(suggestionId),这样,在列举建议的评论时,咱们就能够查找全部suggestionId的建议。
  • 评论时间(time) --- 进行展现的排序。
  • 评论人(person) --- 用于在前台进行展现。
  • 评论内容(content) --- 存储评论的内容。
  • 评论ID --- 惟一的标识这一条评论。
  • 评论类型(type) ---  管理员的type为一、而用户的type为2。

  

      (4)管理员存储

  • 管理员名称 ---(用于显示)能够是手机号。
  • 管理员Id(id)--- 用于寻找全部的本身关注的产品。
  • 管理员类型 --- 若是是1,表示这是一个管理员,若是是2,表示这是一个用户。
  • 管理员建立的产品Id(产品Id是在建立的时候分配的) --- 只有建立了这个产品的人才能对这个产品进行操做。

  (5)用户存储

  • 用户名称 --- 用于显示。
  • 用户Id --- 用于查找本身的全部的建议。
  • 用户类型 --- 若是是1,表示这是一个管理员,若是是2,表示这是一个用户。

 

  即在数据库中须要创建5个集合,这样咱们就能够进行简单的系统操做了。能够看到,上面的数据库的设计尽可能是扁平化的,而没有进行扎推。 

  而且咱们应当提升可重用性,不要在不一样的表中创建了太多相同的内容,这是不合适的。

 

 

八、 ant.design究竟应该怎么用? 

  咱们在使用的时候应该尽量多地去考虑其源码,这样,咱们才能更理解,也能有所学习。

  

 

 

九、  管理系统和用户端对于建议的状态管理应该是怎么样的? 

用户端

  • 用户提交建议,用户端显示为已提交

  没错,用户的权限很小,用户只能提交以后,看到其答案已经提交了,后续的工做就彻底取决于管理员了。

管理端

  • 用户提交了建议以后,管理端显示为新建议。 
  • 这时管理员应该及时的将状态修改成审核中,这样用户就能够直接看到建议的最新状态,接着和团队、小组成员进行讨论建议的可实施性,也许会持续一段事件,并做出及时地回复。
  • 讨论结束以后,管理员能够决定审查未经过,但必定要给予用户进行充分的解释,那么这个问题就能够关闭了或者是决定已采纳。及时的将信息显示在用户端界面。 
  • 若是是 已采纳,那么管理端就须要及时解决,而后将信息及时通知给客户端 。 

 

因此说,对于建议的管理,主动权彻底在管理端,这样才会比较好控制数据,不至于混乱,就像redux的单向数据流的方式是同样的。

 

 

十、登陆、注册这部分应该怎么作?  管理员、用户、产品、建议的ID怎么设置能够保证没有大的问题? 怎么保证不会重复? 表的设计是否有问题。

  一、 对于惟一ID,咱们可使用 uuid  (在项目中咱们直接 npm install uuid --dev 便可),这个工具能够帮助咱们快速解决问题,而且ID是不会重复的。

  二、 对于登陆、注册的问题,咱们能够把这个反馈的网站看作一个黑盒子,而后只须要对之有肯定的输入, 就能保证其输出,因此,咱们能够将其i做为插件来想, 对于登陆、注册的事情由不一样的网站来解决便可。 而咱们须要作的就是在后台处理好便可。  而在管理端仍是比较容易理解的,就是必须登陆注册才能查看产品等等。

 

 

十一、 uuid介绍。

参考文章: https://www.npmjs.com/package/uuid
           http://www.jianshu.com/p/d553318498ad

 

  UUID是128位的全局惟一标识符,一般由32字节的字符串表示。它能够保证时间和空间的惟一性,也称为GUID,全称为:UUID ―― Universally Unique IDentifier,Python 中叫 UUID。

  

 

十二、 如何实现代理服务器呢?

  很简单,咱们打开了两个项目,可是咱们只但愿在一个项目的服务器上处理各类服务器、数据库的数据, 这样,咱们直接将次服务器直接代理到主要的服务器便可。以下所示:

第一步:

npm install  http-proxy-middleware --save-dev

 

第二步:

在dev-server.js中,须要引入 http-proxy-middleware,而后:

var proxyTable = {
        '/api': {
              target: 'http://127.0.0.1:8000/', // 本地node服务器
              changeOrigin: true,
              pathRewrite: {
                   '^/api': '/'
              }
          }
      };
        

上面就是咱们的基本设置,固然,在proxyTable中能够代理多个服务器,但这里咱们只须要一个。 思路就是对api代理,而后发出的时候再重写。

 

// proxy api requests
Object.keys(proxyTable).forEach(function (context) {
  var options = proxyTable[context]
  if (typeof options === 'string') {
    options = { target: options }
  }
  app.use(proxyMiddleware(options.filter || context, options))
})

ok! 就是这么简单了,这样就能够完成服务器的代理了。 

 

 

下面咱们就能够发出一个请求了:

  

    componentWillMount () {
        fetch("/api/getAllProduct", {
            method: "GET"
        }).then(function(res) {
            console.log('进入');
            return res.json();
        }).then(function (data) {
            console.log(data);
            if (data.code == 200) {
              console.log('获取到全部产品' ,data.data);
            } else {
              console.log(data.message);
            }
        })
    }

 

即: 这里咱们在 localhost: 3000 的服务器下发出了指向 localhost:8000 服务器的资源。

 

 

1三、屡次看到服务器崩溃! 为何呢?

 

即如上所示,乍一看彷佛并无什么解决的办法,都是不知道的文件,可是,若是咱们仔细看,仍是能够发现致使问题的地方的, 好比这里咱们能够看到 router 下的 index.js 问题, 接着锁定 product.js  的问题, 再去追究,能够发现,咱们在出错的时候,没有及时关闭mongodb数据库,这样,就会报错,修复了这个问题以后,就能够正常获取数据了。

 

 

14、在使用redux的过程当中,遇到了一个问题。 即在一个问题列表页,点击每一条链接以后,能够进入这个列表的详情页,那么如何在详情页获取到数据呢? 目前的方法是这样的, 即在列表页的Link进行路由跳转的时候,将这条列表的suggestionId传递到详情页中去,而后在 componentWillMount() 这个钩子函数中使用下面的方法:

    componentWillMount () {
        const suggestionId = this.props.location.query.suggestionId;
        
        this.props.filterSug(suggestionId);

        console.log(suggestionId);
    }

 

即获取到当前的 suggestionId, 而后经过一个 action 筛选 store 中的这条建议。

 

function handleSuggestions (state = {allSuggestions: [], filteredSug: []}, action) {
    switch (action.type) {
        case 'ADD_ALL_SUGGESTIONS': 
            const newSug = Object.assign([], action.data);
            return {
                allSuggestions: newSug
            }
        case 'FILTER_SUGGESTION': 
            return {
                allSuggestions: state.allSuggestions,
                filteredSug: state.allSuggestions.filter(function (item, index) {
                    return item.suggestionId == action.id;
                })
            }
        default: 
            return state;
    }
}

 

 

  接着,咱们在再从store中获取这条建议。以下:

function mapDispatchToProps (dispatch) {
    return {
        filterSug: (id) => dispatch(
            filterSuggestion(id)
        )
    }
}

function mapStateToProps (state) {
    return {
        filteredSug: state.handleSuggestions.filteredSug
    }
}

  

  接着,咱们就能够在render函数中使用这个 prop 了,即:

const {filteredSug} = this.props;

  可是这样致使的一个问题就是: 经过 filteredSug.map 调用时,发现会报错,即 没法读取 map 所在的值,他是 undefined 的。   

  而若是咱们使用下面的方法:

const {filteredSug = [] } = this.props;

 

  即便用es6中的默认值的方法,这样就不会报错了,而且能够正常显示这条建议。 这是为何? 我以前的想法是: 由于在 store 中存储时,就会有一个默认值,因此,就算是直接获取应该也是一个空的数组,而不是undefined啊,为何这里还须要提供一个默认值呢? 

     

  首先能够肯定的是,在store中的state发生变化的时候,就会及时的经过页面进行最新的渲染,这样页面就会及时的变化,即最开始 filteredSug 是 [], 而后当reducer处理完了 action 以后,就会改变state,这样 filteredSug 就成了有一个对象元素的数组了,这样咱们就能够进行map了。

   

  之因此页面会随着数据发生变化,是由于页面对数据有了一个订阅,来监听变化。getState函数能够获取当前的state。 

  

  下面,咱们须要测试的就是在 const {} = this.props;和后面的 mapStateToProps 获取的速度问题(即谁先谁后),测试以下:

在 componentWillMount中添加下面的语句:

        console.log('在componentWillMount中的时间', new Date().getTime());

 

在render函数下添加下面的语句

        const {filteredSug} = this.props;

        console.log('render函数时间', new Date().getTime());

 

在 mapStateToProps中添加下面的语句

function mapStateToProps (state) {
    console.log('获取store中的state的时间', new Date().getTime(), state);
    return {
        filteredSug: state.handleSuggestions.filteredSug
    }
}

 

 

而后,咱们开始测试,加载这个页面,结果以下:

  这里的总体思路很是简单,就是首先进入页面,而后第一步就获取到当前的state, 这样的好处在于,第一步获取到以后就能够在后面的各个钩子函数、render函数中使用了,可是咱们能够发现一个问题,就是在handleSuggestions这个reducer里只有allSuggestions可是并无filteredSug。 第二步进入了componentWillMount钩子函数中,这样就能够直接出发filter咱们想要的suggestion的action了。 第三步就是开始render。 因为在第一步的过程当中就没有获取到filterSug,因此在render的时候,就能够发现map的是一个undefined值。 第四步就比较有意思了,就是在咱们以前触发了一个action,因此又在render以后,从新接收到了新的store,这样,就又会从新渲染出来新的render。 咱们能够发现,这个 fiterdSug 是存在的,可是以前若是没有赋默认值,那么就在前面报错了,也就没有后面的步骤了。


     

问题缘由:

  其实如今就比较好理解了,问题就是处在 reducer 那里,咱们在获取到全部的建议的时候,并无把当前 state 的全部值返回到一个新的state了,因此就致使了在进入 detail 页面的时候,接收不到 filteredSug,解决问题的方式很简单,以下:

function handleSuggestions (state = {allSuggestions: [], filteredSug: []}, action) {
    switch (action.type) {
        case 'ADD_ALL_SUGGESTIONS': 
            const newSug = Object.assign([], action.data);
            return {
                allSuggestions: newSug,
                filteredSug: []
            }
        case 'FILTER_SUGGESTION': 
            return {
                allSuggestions: state.allSuggestions,
                filteredSug: state.allSuggestions.filter(function (item, index) {
                    return item.suggestionId == action.id;
                })
            }
        default: 
            return state;
    }
}

这样,咱们就能够在render中使用filterSug的时候不须要使用默认值,而后就map了,由于开始map的时候,什么都没有,因此就不会渲染,而后store接收到action以后,触发了新的state,这样就可使得咱们的filterSug成为了一个新的值,页面就会渲染出来了。

  

 

那么 connect 的这个源码是怎样的呢? 为何在进入页面的时候,能够保证在 componentWillMount 钩子函数以前就能够已经获取到了 store 中的state呢? 

 

猜测一: 因为在react中的组件里,constrctor钩子函数式最先被调用的,因此这里获取store的步骤多是在 constructor 时调用的。 由于connect是react-redux的方法, 而react-redux是另一个库,因此只能利用react的现有的api。

测试验证

  方法: 在constructor钩子函数中打印一下时间,再在 connect 的相关函数中打印一下时间, 若是说 connect 中的时间在后,那么就是对的。

   这个结果很意外,为何能够首先得到 state 呢? 不是应该首先得到conscructor的吗? 而后利用这个钩子函数获取到state? 

 

猜测二: 既然state是最早获取到的, 那么就是说它在原来的组件的基础上包装了一层。 

  这个确实很简单了,在最开始学习redux的过程当中,就已经学习到了下面的概念:组件包括UI组件和容器组件,前者的做用彻底是用于展现的, 而容器组件的做用就是使用状态管理工具如redux来命名的,即在使用redux时,就须要先将全部的数据以props的形式传递到各个容器组件中(实际上并非这样的,在react中,有一个context的概念,就是传递数据,不须要使用props层层传递,而只须要使用一个 context 就能够以最快的速度把想要传递的数据给到各个子组件中了), 而后容器组件再将之做为props传递给UI组件, 而且咱们在使用redux时,不管是发送dispatch,仍是接受store中的数据,都须要经过这一层。 

  到这里这个问题就很清楚了,就是首先,connect的容器组件首先将获取到的数据传进来放在props中,而后再开始建立内部的UI组件,而后内部须要相应的props时,直接从这个中间层来获取就能够了。因此constructor的创造时间是晚于state传递进来的时间的。

相关文章
相关标签/搜索