用好React,你必需要知道的事变

容器性组件(container component)和展现性组件(presentational component)

运用React编写组件时,我们须要有意识地将组件划分为容器性组件(container component)和展现性组件(presentational component),如许有助于我们在编写组件时,越发明白这个组件应当担任哪些事变。

容器性组件,担任营业流程逻辑的处置惩罚,如发送收集要求,处置惩罚要求数据,将处置惩罚过的数据通报给子组件的Props运用。同时,容器性组件供应源数据的要领,以Props体式格局通报给子组件,当子组件的状况变动引劈头数据的变化时,子组件经由过程挪用容器性组件供应的要领同步这些变化。

展现性组件,担任组件的表面,也就是组件怎样衬着,具有很强的内聚性。展现性组件不关心衬着时运用的组件属性(Props)是怎样猎取到的,它只需晓得有了这些Props后,组件应当怎样衬着就足够了。属性怎样猎取,是容器性组件担任的事变。当展现性组件状况的变化须要同步到源数据时,须要挪用容器性组件中的要领,这个要领平常也是经由过程Props通报给展现性组件。

比方,一个Todo项目,有一个Todo组件和一个TodoList组件,Todo组件是一个容器性组件,担任从服务器端猎取待办事项列表,猎取到待办事项列表后通报给TodoList显现。当在TodoList中新建一项待办事项后,须要经由过程TodoList 的 Props,挪用Todo组件中保留待办项目的要领,将新建的待办项目同步到服务器端。

容器性组件和展现性组件能够互相嵌套,一个容器性组件能够包括多个展现性组件和其他的容器性组件;一个展现性组将也能够包括容器性组件和其他的展现性组件。如许的分工,能够使与组件衬着无直接关系的逻辑由容器性组件鸠合担任,展现性组件只关注组件的衬着逻辑,从而使展现性组件更轻易被复用。关于异常简朴的页面,平常只需一个容器性组件就足够了;但关于担任页面,则须要多个容器性组件,不然一切的营业逻辑都在一个容器性组件中处置惩罚的话,会致使这个组件异常庞杂,同时这个组件猎取到的源数据,能够须要经由许多层的组件Props的通报,才抵达终究运用的展现性组件。

Props、State和组件的一般属性

Props、State的观点都很清楚,组件的一般属性是指在组件中直接挂载到this下的属性。实在,Props和State也是组件的两个一般属性,因为我们能够经由过程this.props 和 this.state 直接猎取到。那末Props、State 和 组件的其他一般属性,离别应当在什么场景下运用呢?

Props和State都是用于组件衬着的,也就是说,一个组件终究长成什么样,取决于这个组件的Props和State。Props和State的变化都邑触发组件的render要领。但这两者也是有区分的。Props是只读的数据,它是由父组件通报过来的;而State是组件内部本身保护的状况,是可变的。State能够依据Props的变化而变化。假如组件中还须要其他属性,而这个属性又与组件的衬着无关(也就是render要领中不会用到),那末就能够把这个属性直接挂在到this下,而不是作为组件的一个状况。

比方,组件中须要一个定时器,每隔几秒转变一下组件的状况,就能够定义一个this.timer属性,以备在componentWillUnmount时,消灭定时器。

setState 异步性

React官网提到,this.state和this.props的更新多是异步的,React能够会出于机能斟酌,将多个setState的挪用,合并到一次State的更新中。所以,不要依靠this.props 和 this.state的值盘算下一个状况。援用官网的一个代码示例:

// Wrong
this.setState({
  counter: this.state.counter + this.props.increment,
});

假如一定要这么做,能够运用另一个以函数作为参数的setState要领,这个函数的第一个参数是前一个State,第二个参数是当前接收到的最新Props。以下所示:

// Correct
this.setState(function(prevState, props) {
  return {
    counter: prevState.counter + props.increment
  };
});

在挪用setState以后,也不能马上运用this.state猎取最新状况,因为这时候的state极能够还没有被更新,要想保证猎取到的state是最新的state,能够在componentDidUpdate中猎取this.state。也能够运用带用回调函数参数版本的setStatesetState(stateChange, [callback]),回调函数中的this.state会保证是最新的state。

componentWillReceiveProps

当组件的属机能够发作变化时,这个要领会被挪用。这里说能够,是因为父组件render要领每次被挪用时,子组件的这个要领都邑被挪用(子组件第一次初始化时除外),但并不一定每次子组件的属性都邑发作变化。假如组件的State须要依据Props的变化而变化,那末这个要领就是最适合这个这个逻辑的处所。比方当Props变化时,组件的State须要重置,就能够在这个要领中挪用this.setState()来重置状况。须要注重,在这个要领中挪用this.setState()并不会从新触发componentWillReceiveProps的挪用,也不会致使render要领被触发两次。平常情况下,接收到新Props会触发一次render,挪用this.setState也会触发一次render,但在componentWillReceiveProps中挪用this.setState,React会把底本须要的两次render,合并成一次。

shouldComponentUpdate

这个要领常作为优化React机能运用。当shouldComponentUpdate返回false时,组件本次的render要领不会被触发。能够经由过程在这个要领中比较前后两次state或许props,依据现实营业场景决议是不是须要触发render要领。

React供应了一个React.PureComponent组件,这个组件重写了shouldComponentUpdate,会对前后两次的state和props举行浅比较,怎样不一致,才会返回true,触发后续的render要领。这里的浅比较指,只会对state和props的第一级属性举行比较(运用!==),这满足平常的运用场景。假如你的组件继续了React.PureComponent,但在setState时,传入的state是直接修正的原有state对象,就会因为依旧满足浅比较的条件,而不会从新触发render要领,致使终究DOM和state不一致。比方state={books: ['A','B']},在setState时,运用this.setState({name: this.state.books.push('C')})直接修正books对象,如许虽然books内容发作了修正,但因为对象援用并没有变化,所以依旧满足浅比较条件,不会触发render要领。

平常情况下,让shouldComponentUpdate返回默许的true是不会有太大题目的。虽然如许能够致使一些不必要的render要领被挪用,但render要领直接操纵的是假造DOM,只需假造DOM没有发作变化,并不会致使实体DOM的修正。而JS慢是慢在实体DOM的修正上。只需你的render要领不是很庞杂,多挪用频频render要领并不会带来多大的机能开支。

render

父组件每次render要领被挪用,或许组件本身每次挪用setState要领,都邑触发组件的render要领(条件是shouldComponentUpdate运用默许行动,老是返回true)。那末组件每次render,是不是是都邑致使实体DOM的从新建立呢?答案是,不是!

React之所以比直接操纵DOM的JS库快,缘由是React在实体DOM之上,笼统出一层假造DOM,render要领实行后,获得的是假造DOM,React 会把组将当前的假造DOM构造和前一次的假造DOM构造做比较,只要存在差别性,React才会把差别的内容同步到实体DOM上。假如两次render后的假造DOM构造保持一致,并不会触发实体DOM的修正。

React速度快的缘由,另有一个是它精彩的Diff算法。规范的比较两棵树的Diff算法的时候庞杂是 O(n3) 。而React基于异常符合现实场景的两个假定,就将Diff算法的时候庞杂度降到了靠近O(n)。这两个假定是:

  1. 假如两个组件或元素范例差别,那末他们就是完整差别的树,不须要再比较他们的子节点。比方,<Article><Comment>将发作是两个完整的树状构造;<div>children</div><p>children</p>也是两个完整差别的树。这类情况下,组件会被完整重修,旧的DOM节点被烧毁,组件阅历componentWillUnmount(),然后从新建立一棵新树, 组件阅历 componentWillMount()componentDidMount()
  2. 能够为组件或元素设置key属性,key用来标识这个组件或元素。key不须要全局唯一,只须要在兄弟组件或兄弟元素间保证唯一性就能够。key常用到鸠合(List)元素中。比方:
<ul>
<li key='a'>Book A</li>
<li key='b'>Book B</li>
</ul>

当在第一个位置插进去一条纪录Book C 时,

<ul>
<li key='c'>Book C</li>
<li key='a'>Book A</li>
<li key='b'>Book B</li>
</ul>

因为有key的标识,React晓得此时新增了一条纪录,会建立一个新的<li>元素,并把它插进去到列表中的第一个位置。假如没有设置key,React并不晓得是新增了一条纪录,照样本来的两条纪录完整替换成新的三条纪录,或许其他越发庞杂的修正场景。React须要自上而下的比较每一条纪录,如许每次比较节点都差别,所以须要修正两次节点,然后再新增一个节点,效力明显要差许多。

这里同时揭露了另一个题目,不要运用元素在鸠合中的索引值作为key,因为一旦鸠合中元素递次发作转变,就能够致使大批的key失效,进而引发大批的修正操纵。

怎样发送收集要求

当我们须要从服务器猎取数据时,我们应当在组件的哪个生命周期要领中发送收集要求呢?React官网上提到,能够在componentDidMount中发送收集要求,这也是平常情况下的最好实践。有些人也会把发送收集要求放在componentWillMount中,而且以为这个要领先于componentDidMount挪用,所以能够更快地猎取数据。个人以为,这类运用要领平常也是没有题目的,但在一些场景下会出现题目,比方须要在服务器端衬着时,componentWillMount会被挪用两次,一次是在Server端,一次是在Client端。可参考这篇文章

迎接关注我的民众号:老干部的大前端,领取21本大前端精选书本!

《用好React,你必需要知道的事变》

    原文作者:艾特老干部
    原文地址: https://segmentfault.com/a/1190000010305632
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞