React组件传值与路由传参

2018-08-02更新:

不难想象,这时候联系两个页面的纽带就是URL值或共同的父组件。这就引发了两个解决方法:context和React-Redux。

这里表述的不对
URL传参是react-router,也有很多种方式实现:参考React Router 页面传值的四种方法
redux的底层机制还是context,只是封装了dispatch方法和state


基于上上周的学习,搭建了webpack+React的项目环境,这两周主要就在写业务代码并基于业务需求继续学习React和穿插的其他一些小知识,React入门主要参考了以下几个系列的文章,在理解页面DOM渲染,视图层等概念的基础上,还是非常浅显易懂的:
玩转 React(一)- 前言
React 深入系列1:React 中的元素、组件、实例和节点

以下记录了对一些概念的理解以及部分思考:

  1. props和state有什么区别,如何使用,父子组件之间传值;
  2. 页面之间如何传值,怎么拿到类似全局变量的一个值(react-redux相关)
  3. 如何触发render更新组件,如何避免不必要的render调用(理解生命周期)
  4. 几个细节(props覆盖,条件表达式简化,组件初始化,表单重置,render嵌套HTML标签)

1. props和state有什么区别,如何使用,父子组件之间传值

我知道React是一个视图层的框架,在之前使用的JQuery中,我们通过编写HTML代码来设计网页的结构,通过 jquery选择器$("#")以及getElementById等 api 来获取某个节点,通过节点的 innerHTML,innerText,appendChild 等属性或者方法来更新视图,但是在React,我只需要关注数据的更新,React会帮我完成视图的更新。
所以,在我看来,定义一个React组件,可以分为两部分,一部分用来定义处理数据,一部分用来render返回react元素渲染DOM。
因此,各组件和页面间的传值成了我首要考虑的问题也是遇到问题最多的地方,而组件根据props和state计算得到对应页面的UI,这两个参数有什么区别,如何定义呢?

  1. props 是组件对外的接口,state 是组件对内的接口,组件通过state参数渲染元素,上下层组件通过props参数传递数据,state是可变的,props是只读属性;
  2. 不能直接修改state,采用setState方法,state属性改变会重发render更新组件做到视图和数据的绑定;
  3. 调用setState,组件的state并不会立即改变,setState只是把要修改的状态放入一个队列中,React会优化真正的执行时机,并且React会出于性能原因,可能会将多次setState的状态修改合并成一次状态修改。所以不能依赖当前的state,计算下个state。

因此一个完整的父子组件之间传值的流程如下(以主组件调用侧栏组件为例):

  • 子组件props属性定义及类型校验
SlideFrame.propTypes = {
  width: React.PropTypes.any,  //宽度
  title: React.PropTypes.string,  //标题
  show: React.PropTypes.bool,  //是否显示
  hasMask: React.PropTypes.bool,  //是否有遮罩层
  onClose: React.PropTypes.func,  //点击遮罩层或右上方x时触发的事件
  content: React.PropTypes.oneOfType([React.PropTypes.func, React.PropTypes.string]),  //内容component,包裹后的元素添加this.props.close方法进行侧滑关闭
  afterClose: React.PropTypes.func,  //关闭后触发的事件,用于更新外层的show值
  params: React.PropTypes.object,  //外部传入内部组件props
  hasFooter: React.PropTypes.bool  //是否有低端操作区
};

SlideFrame.defaultProps = {
  width: '50vw',
  onClose: ()=>{},
  okText: '保存',
  cancelText: '取消',
  hasMask: true,
  afterClose: ()=>{},
  params: {},
  hasFooter: true
};
  • 父组件传值
 <SlideFrame title={ showSlideFrameContent.isNew ? messages('rep.distribution.details.create')/*新建分配*/ : messages('rep.distribution.details.edit')/*编辑分配*/}
                    show={showSlideFrame}
                    content={ReportDistributionMaintain}
                    onClose={this.closeSlide}
                    params={showSlideFrameContent}
        />
  • 父组件更新state值,重发render更新传入子组件的值
ReportDistributionService.getCopyReportDetail(record.reportLineOID).then((response) => {
      this.setState({
        loading: false,
        showSlideFrameContent :{
          isNew: false,
          reportDetail: response.data,
          reportLineOID: record.reportLineOID
        }
      })
    });
    ReportDistributionService.getDistributionPeopleList(page, pageSize,record.reportLineOID).then((response) => {
      response.data.map((item,index) => {
        item.index = index + page * pageSize + 1;
        item.key = item.userOID;
        item.departmentName = item.department.name;
      });
      this.setState({
        loading: false,
        showSlideFrameContent :{
          isNew: false,
          pagination: {
            total: Number(response.headers['x-total-count']) ? Number(response.headers['x-total-count']) : 0,
            current: this.state.page + 1
          },
          distributionPeopleList: response.data,
          reportLineOID:record.reportLineOID
        }
      });
    });
  • 子组件接收props
componentWillReceiveProps(nextProps) {
    //console.log("子组件接收props");
    this.setState({loading: true});
    // props每变一次就会调用一次,所以赋值如果写一起会被空值覆盖
    if (nextProps.params.reportDetail) {
      console.log(nextProps.params.reportDetail);
      this.setState({
        isNew: nextProps.params.isNew,
        reportDetail:nextProps.params.reportDetail,
        reportLineOID: nextProps.params.reportLineOID
      }, () => {
        if(nextProps.params.reportDetail.dataArea === "5"){
          this.getCopyReportCorporation();  //获取 单条 报表副本 已选法人列表
        }
        if(nextProps.params.reportDetail.dataArea === "8"){
          this.getCheckedSetOfBookList();  //获取 单条 报表副本 已选账套列表
        }
        if(nextProps.params.reportDetail.dataArea === "4" || nextProps.params.reportDetail.dataArea === "6"){
          this.getCheckedDepList(); //获取 单条 报表副本 已选部门列表
        }
      });
      this.setState({loading: false});
    }
    if (nextProps.params.newCopyReportDetail) {
      this.setState({
        loading: false,
        isNew: nextProps.params.isNew,
        newCopyReportDetail:nextProps.params.newCopyReportDetail
      });
    }
    if (nextProps.params.distributionPeopleList) {
      this.setState({
        loading: false,
        isNew: nextProps.params.isNew,
        pagination: nextProps.params.pagination,
        reportLineOID: nextProps.params.reportLineOID,
        data: nextProps.params.distributionPeopleList
      });
    }
  }

这里有一个小细节,因为更新state的时候数据必须分两次拿到,导致传入的props数据每次都有一个为空,如果取值时也一次性赋值,则会覆盖

参考文章:React中state和props分别是什么?

2.页面之间如何传值,怎么拿到类似全局变量的一个值(react-redux相关)

这周的业务中有一个需求,是拿到当前的语言环境,是中文还是英文,显然,这应该是一个类似全局变量的值;另一个需求,是讲当前页active的tab值通过路由跳转后传到详情页。
上面是父子组件之间的传值,尚有一个公用的接口可以传递数据,那两个没有联系的页面或者说兄弟组件之间如何传值呢,要传值,就要找到联系他们的纽带,不难想象,这时候联系两个页面的纽带就是URL值或共同的父组件。这就引发了两个解决方法:context和React-Redux。

关于context,这篇文章说的很清楚React 组件通信之 React context

React是基于单向数据传递的,一般对于兄弟组件之间的通信,是通过它们共同的祖先组件进行的,即状态提升Lifting State Up,状态提升的意思是,当组件 A 需要依赖另外一个组件 B 的内部状态,而他们又不是父子关系时,需要将组件 B 的内部状态提升到他们公共的祖先组件中管理。这样他们就都可以通过属性接收到这份数据了。
当组件 B 需要对数据进行变更时,可以通过函数属性来通知祖先组件对数据更新,然后重新传递给子组件。

但但组件嵌套比较复杂的时候,这个方法着实很麻烦,context则能做到让组件树全局共享某状态。
context的使用方法也很简单,在顶部父组件声明context,那它的所有子组件可以通过 this.context 直接获取得到,项目业务中采用这个方式存取路由:

//顶部父组件
Main.childContextTypes = {
  router: React.PropTypes.object
};
//子组件
ReportDistribution.contextTypes = {
  router: React.PropTypes.object
};
关于React-Redux

首先,Redux 和 React-redux 并不是同一个东西。Redux 是一种架构模式(Flux 架构的一种变种),它不关注你到底用什么库,你可以把它应用到 React 和 Vue,甚至跟 jQuery 结合都没有问题。而 React-redux 就是把 Redux 这种架构模式和 React.js 结合起来的一个库,就是 Redux 架构在 React.js 中的体现。

参考文章:跟着例子一步步学习redux+react-redux
(这里没看完,待更新。。。)

另外还有一种解决方式:ref属性 ,这个和context一样是react官方不推荐使用的属性,因为可能会在未来的版本更新中被取消。

参考文章:React的Refs方法获取DOM实例 和 访问子组件方法及属性

3.如何触发render更新组件,如何避免不必要的render调用(理解生命周期

上面了解到,当state状态更新时,react会重发render,因此,在业务逻辑中常常采用子组件中componentWillReceiveProps接收props,更新state,触发重新渲染的方式更新视图,但是调试的输出的时候经常发现触发了不止一次,又或者当新建和不同表格行的编辑都共用一个组件时,没有正确的重发render导致表单无法重置。所以render和state的关系到底是什么??
这篇文章或许会有所帮助,【react】利用shouldComponentUpdate钩子函数优化react性能以及引入immutable库的必要性,由于业务有点复杂这里并没有来得及采用这个函数做优化了,但是也值得一看。

4.几个小tips

  1. 表达式优化
    !!相当于Boolean()
    eg: this.props.params.i ? true : false写成 !!this.props.params.id比较好
  2. render函数代码中嵌套多个HTML 标签,需要使用一个标签元素包裹他

(axios
react-redux
react-router
待更新。。。)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335