react 使用的小建議


  • 使用pureRender,setState和Immutable.js來操作state

Immutable 中文意思不可變。

不能直接修改state的值,要用setState 和Immutable

react 官方要求不要直接修改state,比如this.state.name = "suyuan"是錯誤的寫法,應該用this.setState({name, "suyuan"});

原因1.其實state並不是不可變的,官方是希望你把他當做不變來用,因為只有setState的時候才會發生消息給react 來re-render,this.state.name="bianhua" 不會引起re-rener;

原因2.本來不管你數據有沒有變化, setState就一定會重新渲染,為了提高性能一般會引去pureRender技術(本文其他章節有描述),該技術是進行淺比較(根節點的地址),p1,p2,p3

 

假如你這樣錯誤的寫:

let p1=state.p1;

p1.name = "bianhua";

this.setState({p1:p1});

用了pureRender技術后,react 發現p1沒有變化(p1的地址沒變,不是新對象),也不會re-render

所以你應該這樣寫

let p1=JsFrom(state.p1);

p1.name = "bianhua";

this.setState({p1:p1});

這篇文章描述了immutable

https://zhuanlan.zhihu.com/p/20295971?columnSlug=purerender

 

使用PureRenders

Example Mixin 混入類s:

var PureRenderMixin = require('react-addons-pure-render-mixin');
React.createClass({
  mixins:[PureRenderMixin],
  render: function() {
    return <div className={this.props.className}>foo</div>;
  }
});

 

Example using ES6 class syntax:

import PureRenderMixin from 'react-addons-pure-render-mixin';
class FooComponent extends React.Component {
  constructor(props) {
    super(props);
    this.shouldComponentUpdate=PureRenderMixin.shouldComponentUpdate.bind(this);
  }
  render() {
    return <div className={this.props.className}>foo</div>;
  }
}
 
        

或者用pure-render-decorator 裝飾器

import {Component} from 'react';

import pureRender from 'pure-render-decorator';

@pureRender
export default class Test extends Component {
  render() {
    return <div />;
  }
}
 
        

a. 優化了性能

PureRenderMixin 重寫了shouldComponentUpdate,只有在props或者state真正改變的時候才會重新render,這個在react性能上是一個優化,從此以后你可以大膽的setState,而不需要寫這樣的代碼

if (this.state.someVal !== computedVal) { // 判斷是否需要setState,因為每次setState都會引起render,不管你的數據是不是真的變化了

    this.setState({someVal: computedVal})

}

 

 

b.有一點要注意,繼承PureRenderMixin要求render 必須是純函數,當然本來react官文就說render should 純函數,這里跟需要時純函數,為什么?

b.1.單純react來講,頁面和state直接反應,如果render不是純函數,就會導致頁面展示和state對不上號,還有去查詢其他關聯數據;

b.2.從繼承混入類來說,如果你寫了下面這種不純的代碼

render: function () {

    //

    if (this._previousFoo !== this.props.foo) { // <-- IMPURE

        return renderSomethingDifferent();

    }

}

 

依賴了一個其他變量this._previousFoo,就會引入bug;

>本來不用混入類,你setState后就算state不改變,也會render,然后再render過程中this._previousFoo的數據變化可能導致頁面發生變化;

>現在引入混入類,完蛋了,setState后,state數據不變也就不會render了,頁面不變了,和你希望的不一樣了。

所以render要純正,除非上面this._previousFoo 你保證永遠不變,是個const

 

c 還有個重要的事情要說,我覺得太重要了,PureRenderMixin混入類 只會進行淺比較,就是C++里面的指針(地址比較),如果你修改了一個數組的某個item,你其實是希望render的,但是PureRenderMixin 認為數組的地址沒變,數組也就沒變,也就沒render;怎么辦?

c.1.組件創建新對象--切片 array.silce()

c.2.object創建新對象-- var copy = Object.assign({}, obj);

c.3.react 自帶的操作數據的方式 https://facebook.github.io/react/docs/update.html

c.4..第三方庫Immutable.js 和 mori

 

這里有個坑,用了purRender有時候無效,比如組件下面包含了子組件的時候,this.props.children 就算數據不變,對象也不是原來的對象的,為了優化某個組件的性能我還特意重寫了shouldComponentUpdate,把props.children 排除了,這個可能是purRender的原則,看了下源代碼完全沒搞明白,所以假如render不純的話(比如使用了this.name)我估計還是會重寫渲染,有興趣會試驗一下

 

這里插入一句,redux中的mapToState() 做為reducer和Component的橋梁,reducer 在返回newState后雖然肯定會執行mapToState,但是mapToState中return出去的數據{xkey:xvalue},如果xvalue的地址沒有改變,mapToState關聯的Component也不會render;

 

適當的使用context,方便給子組件傳變量

父組件給子組件上下文定義個變量,子組件聲明這個變量,就可以使用;

方便數據傳遞,不需要通過props一層層塞進去

// ------Component A

class A extends React.Component {

// add the following property

static childContextTypes = {

  user: React.PropTypes.object.isRequired

}

 

// add the following method

  getChildContext() {

    return {

      user: this.props.user  //children 組件可以使用this.context.user

    }

  }

 

  render() {

    <div>{this.props.children}</div>

  }

}

 

// -----Component D

class D extends React.Component {

// add the following property

static contextTypes = {

  user: React.PropTypes.object.isRequired

}

 

  render() {

    <div>{this.context.user.name}</div>

  }

}

 

 

或者

function D(props, context) {

  return (

    <div>{this.context.user.name}</div>

  );

}

D.contextTypes = {

  user: React.PropTypes.object.isRequired

}

 

使用propTypes 和defaultProps

class Greeting extends React.Component {

  static propTypes = {
    name: React.PropTypes.string
  }

  static defaultProps = {
  name: "suyuans"
} render() {
return ( <h1>Hello, {this.props.name}</h1> ); } }

 

React 可以幫你檢測傳入的props是否有誤,自己在使用組件時候也會更清晰需要傳入什么props

不過要記得在編譯線上版本的時候設置環境變量NODE_ENV="production",避免影響線上的性能 

 

減少操作state

所有的編程語言在寫代碼的過程中,都希望是無狀態的,純函數的方式;

React 提供了setState 接口,但是我們還要是減少他的使用;

如果state使用不好可能導致組件復雜混亂,re-render 失控;

比如在 componentDidMount() or componentDidUpdate()生命周期中使用setState1是控制不好可能發生死循環,2是加入子組件也類似的使用,子子組件等刷新的次數會成2的冪次方增加;

不過使用state是必不可少的,但是一定要封裝好state,確保只有本組件可見;

假如父組件需要知道一些關於子組件的state,這就破壞了組件的結構,意味着組件的抽象失敗,可以需要對該組件重構

 

集中管理state

在工作中,父組件需要知道子組件的state或者信息是必不可少的,一般我們把state完全集中在一個地方(一般最頂層)控制,

父組件通過props向子組件傳遞信息,

這就是Flux 架構模型:用集合倉庫來管理state,用action事件來驅動state的更新,這時候state的存儲和操作都是原子性的,任何state的變化都會去重新渲染組件,以達到單向數據流的目的;

意思就是所有組件都依賴props渲染數據,數據源全部來自數據中心state,組件通過 事件、管道、回調、方法、stream來和數據中心通信。

 

這個看起來有點低效,但是react所倡導的就是 js的速度很快和虛擬dom diff的思想;

純props的組件庫會更好的發揮purRender相關的優勢;

 

缺點是 會使得原來一些簡單的通過setSate的事情變的麻煩了,但是優點是單向數據流更直觀的反應了我們的應用。

 

 

這種思想就像flux架構,見下圖

 

 

一旦數據中心store 數據變更了,他就render組件,組件會向子組件傳遞變更的數據

說白了,組件就是處理props 到 頁面的純函數,這樣的組件也更容易使用和測試, 組件正常的展示,正常觸發事件,觸發事件后store數據更新正常 就表明組件一切ok。

如果組件之間通過回調函數來操作,組件之間的耦合性就太高,需要知道對方的一些數據、接口、函數,這樣抽象就失敗了,應該通過action的方式相互通訊。

 

盡量把代碼寫在render中

盡量把componentWillReceiveProps or componentWillMount 下面的代碼寫到render中,

把一些處理props,或者計算setState的處理函數寫到render

永遠不要擔心js的速度,這樣寫的好處很多,減少bug並容易發現bug,減少重復代碼等

// bad

componentWillMount: function () {

    this.setState({

        computedFoo: compute(this.props.foo)

    });

},

componentWillReceiveProps: function (nextProps) {

    this.setState({

        computedFoo: compute(nextProps.foo)

    });

},

render: function () {

    return React.DOM.div({className: this.state.computedFoo});

}


// better

render: function () {

    var computedFoo = compute(this.props.foo);

    return React.DOM.div({className: computedFoo});

}

 

也可以在render 中計算獲取其他的組件,在render 做更多的事情,當然也不要把render搞的太長太大

 

 

MIXIN 或者extend 非常好用

可以用他們來創建復用的功能塊

PureRenderMixin  就是覆蓋了shouldComponentUpdate() 方法

你可能有疑問了,生命周期componentWillMount之類的繼承下來不就遭了,有的我不需要是不是要重寫個空的?放心,mixin特意把生命周期排除出去啊,哈哈 機智啊

比如我們可以把處理state的邏輯放到mixin中,就算只有一個組件只用也ok,這樣可以保證組件內部的無狀態性,通過mixin 來構建有狀態的組件,以后你可以通過修改mixin來更換功能,復用這個組件,mixin這時候有點control層的角色扮演,不過小心的是mixin要被拋棄了,用extend把

 

 

使用類實例的屬性

盡管組件應該是以props為參數的純函數,但是有時候使用實例屬性也是明智的。

有時候this.foo比this.props.foo要更合適;要注意的是PureRender 的時候render下不要這樣使用,原因看之前的內容。

如果這個數據不影響頁面的呈現,這樣用是非常方便的

 

componentWillReceiveProps: function () {

    this._timer = Date.now();

},

onLoadHandler: function () {

    this.trigger("load:time", Date.now() - this._timer);

},

render: function () {

    return React.DOM.img({

        src: this.props.src,

        onLoad: this.onLoadHandler

    });

}

 

上面用一個實例屬性來存放開始時候,以計算load時間,這個時間變化后我也不需要頁面展示渲染,所以可以放在實例屬性中。

一句話,數據變化了,頁面不需要刷新變化的,可以作為實例屬性

數據變化,頁面變化的那就是state了

 

組件通信

父到子  1.props, 2.ref   

子到父  1.回調函數this.props.callback()

兄弟   1.通過父繞一圈,2.采用第三方消息系統如js-signals: https://github.com/millermedeiros/js-signals

 

一切通信,如果用了flux或者redux就解決了

 

 

下面是一些js使用建議

1.遵循es6嚴格模式

2.聲明變量的優先級 const、let

3.箭頭函數代替繁雜的bind

4.可以引用typescript減少bug,當然用了嚴格模式也足夠了

 

推薦個組件庫 antd

推薦使用redux(control、modal層) 管理react(view層s)

 

本文參考了

React建議:http://aeflash.com/2015-02/react-tips-and-best-practices.html

immutable使用:

https://zhuanlan.zhihu.com/p/20295971?columnSlug=purerender

https://www.w3ctech.com/topic/1595


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM