React 組件數據流 && 組件間溝通

使用React咱們首先要知道如何傳遞數據,組件如何溝通,才能展現咱們想要的數據。下面的列子都是使用ES6語法,不懂的同窗須要先學習ES6語法。node

數據流

React是單向數據流,從父節點傳遞到子節點(經過props)。若是頂層的某個props改變了,React會重渲染全部的子節點(未作性能優化)。嚴格意義上React只提供,也強烈建議使用這種數據交流方式。react

Props

props是property的縮寫,能夠理解爲HTML標籤的attribute。請把props當作只讀的(不可使用this.props直接修改props),props是用於整個組件樹中傳遞數據和配置。在當前組件訪問props,使用this.props。在什麼狀況下可使用props,請看組件生命週期git

class Component {
  constructor(props){
    super(props);
  }
  render(){
    return (
        <div title={this.props.title}></div>
    )
  }
}
<Component title="test"/>//調用title就傳進去了

PropTypes

PropsTypes是React中用來定義props的類型,不符合定義好的類型會報錯。建議可複用組件要使用prop驗證!接着上面的列子設置PropsTypes以下:github

class Component {
  ...
}
Component.PropsType = {
  title: React.PropTypes.string,
}

React.PropTypes 提供不少驗證器 (validator) 來驗證傳入數據的有效性。官方定義的驗證器以下,不是使用ES6語法。redux

React.createClass({
  propTypes: {
    // 能夠聲明 prop 爲指定的 JS 基本類型。默認
    // 狀況下,這些 prop 都是可傳可不傳的。
    optionalArray: React.PropTypes.array,
    optionalBool: React.PropTypes.bool,
    optionalFunc: React.PropTypes.func,
    optionalNumber: React.PropTypes.number,
    optionalObject: React.PropTypes.object,
    optionalString: React.PropTypes.string,
    optionalSymbol: React.PropTypes.symbol,

    // 全部能夠被渲染的對象:數字,
    // 字符串,DOM 元素或包含這些類型的數組(or fragment) 。
    optionalNode: React.PropTypes.node,

    // React 元素
    optionalElement: React.PropTypes.element,

    // 你一樣能夠斷言一個 prop 是一個類的實例。
    // 用 JS 的 instanceof 操做符聲明 prop 爲類的實例。
    optionalMessage: React.PropTypes.instanceOf(Message),

    // 你能夠用 enum 的方式
    // 確保你的 prop 被限定爲指定值。
    optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),

    // 指定的多個對象類型中的一個
    optionalUnion: React.PropTypes.oneOfType([
      React.PropTypes.string,
      React.PropTypes.number,
      React.PropTypes.instanceOf(Message)
    ]),

    // 指定類型組成的數組
    optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),

    // 指定類型的屬性構成的對象
    optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),

    // 特定形狀參數的對象
    optionalObjectWithShape: React.PropTypes.shape({
      color: React.PropTypes.string,
      fontSize: React.PropTypes.number
    }),

    // 你能夠在任意東西后面加上 `isRequired`
    // 來確保 若是 prop 沒有提供 就會顯示一個警告。
    requiredFunc: React.PropTypes.func.isRequired,

    // 不可空的任意類型
    requiredAny: React.PropTypes.any.isRequired,

    // 你能夠自定義一個驗證器。若是驗證失敗須要返回一個 Error 對象。
    // 不要直接使用 `console.warn` 或拋異常,
    // 由於這在 `oneOfType` 裏不起做用。
    customProp: function(props, propName, componentName) {
      if (!/matchme/.test(props[propName])) {
        return new Error('Validation failed!');
      }
    }
  },
  /* ... */
});

defaultProps

如何設置組件默認的propssegmentfault

//React提供的crateClass建立方式
var Component = React.createClass({
  getDefaultProps(){
    return {
      //這裏設置defaultProps
    }
  }
})
//ES6
class Component {
  ...
}
Component.defaultProps = {}
//ES7 stage-0
class Component {
  static defaultProps = {
    
  }
  ...
}

state

每一個組件都有屬於本身的statestateprops的區別在於前者之只存在於組件內部,只能從當前組件調用this.setState修改state值(不能夠直接修改this.state)。通常咱們更新子組件都是經過改變state值,更新新子組件的props值從而達到更新。數組

那如何設置默認state?性能優化

//React提供的crateClass建立方式
var Component = React.createClass({
  getInitialState(){
    return {
      //這裏設置初始state值
    }
  }
})
//ES6 && ES7
class Component {
  constructor(){
    this.state = {}//在ES6中的構造函數中初始化,能夠之直接賦值,在其餘方法中,只能使用this.setState
  }
  ...
}

props和state使用方式

儘量使用props當作數據源,state用來存放狀態值(簡單的數據),如複選框、下拉菜單等。架構

組件溝通

組件溝通由於React的單向數據流方式會有所限制,下面述說組件之間的溝通方式。框架

父子組件溝通

這種方式是最多見的,也是最簡單的。

  • 父組件更新組件狀態

父組件更新子組件狀態,經過傳遞props,就能夠了。

  • 子組件更新父組件狀態

這種狀況須要父組件傳遞迴調函數給子組件,子組件調用觸發便可。

代碼示例:

class Child extends React.Component{
  constructor(props){
    super(props);
    this.state = {}
  }
  
  render(){
    return (
      <div>
        {this.props.text}
        <br />
        <button onClick={this.props.refreshParent}>
            更新父組件
        </button>
      </div>
    )
  }
}
class Parent extends React.Component{
  constructor(props){
    super(props);
    this.state = {}
  }
  refreshChild(){
    return (e)=>{
      this.setState({
        childText: "父組件溝通子組件成功",
      })
    }
  }
  refreshParent(){
    this.setState({
      parentText: "子組件溝通父組件成功",
    })
  }
  render(){
    return (
      <div>
        <h1>父子組件溝通</h1>
        <button onClick={this.refreshChild()} >
            更新子組件
        </button>
        <Child 
          text={this.state.childText || "子組件未更新"} 
          refreshParent={this.refreshParent.bind(this)}
        />
        {this.state.parentText || "父組件未更新"}
      </div>
    )
  }
}

codepen例子React組件之父子組件溝通 。

兄弟組件溝通

當兩個組件有相同的父組件時,就稱爲兄弟組件(堂兄也算的)。按照React單向數據流方式,咱們須要藉助父組件進行傳遞,經過父組件回調函數改變兄弟組件的props

方式一

經過props傳遞父組件回調函數。

class Brother1 extends React.Component{
  constructor(props){
    super(props);
    this.state = {}
  }
  
  render(){
    return (
      <div>
        <button onClick={this.props.refresh}>
            更新兄弟組件
        </button>
      </div>
    )
  }
}
class Brother2 extends React.Component{
  constructor(props){
    super(props);
    this.state = {}
  }
  
  render(){
    return (
      <div>
         {this.props.text || "兄弟組件未更新"}
      </div>
    )
  }
}
class Parent extends React.Component{
  constructor(props){
    super(props);
    this.state = {}
  }
  refresh(){
    return (e)=>{
      this.setState({
        text: "兄弟組件溝通成功",
      })
    }
  }
  render(){
    return (
      <div>
        <h2>兄弟組件溝通</h2>
        <Brother1 refresh={this.refresh()}/>
        <Brother2 text={this.state.text}/>
      </div>
    )
  }
}

codepen例子:React組件之兄弟組件溝通

方式二

可是若是組件層次太深(以下圖),上面的兄弟組件溝通方式就效率低了(不建議組件層次太深)。

React提供了一種上下文方式(挺方便的),可讓子組件直接訪問祖先的數據或函數,無需從祖先組件一層層地傳遞數據到子組件中。

class Brother1 extends React.Component{
  constructor(props){
    super(props);
    this.state = {}
  }
  
  render(){
    
    return (
      <div>
        <button onClick={this.context.refresh}>
            更新兄弟組件
        </button>
      </div>
    )
  }
}
Brother1.contextTypes = {
  refresh: React.PropTypes.any
}
class Brother2 extends React.Component{
  constructor(props){
    super(props);
    this.state = {}
  }
  
  render(){
    return (
      <div>
         {this.context.text || "兄弟組件未更新"}
      </div>
    )
  }
}
Brother2.contextTypes = {
  text: React.PropTypes.any
}
class Parent extends React.Component{
  constructor(props){
    super(props);
    this.state = {}
  }
  
  getChildContext(){
    return {
      refresh: this.refresh(),
          text: this.state.text,
      }
    }
  
  refresh(){
    return (e)=>{
      this.setState({
        text: "兄弟組件溝通成功",
      })
    }
  }
  render(){
    return (
      <div>
        <h2>兄弟組件溝通</h2>
        <Brother1 />
        <Brother2 text={this.state.text}/>
      </div>
    )
  }
}
Parent.childContextTypes = {
  refresh: React.PropTypes.any,
  text: React.PropTypes.any,
}

codepen例子:React組件之兄弟組件溝通2

全局事件

For communication between two components that don't have a parent-child relationship, you can set up your own global event system. Subscribe to events in componentDidMount(), unsubscribe in componentWillUnmount(), and call setState() when you receive an event.Flux pattern is one of the possible ways to arrange this.

官網中提到可使用全局事件來進行組件間的通訊,官網推薦Flux(Facebook官方出的),還有Relay、Redux、trandux等第三方類庫。這些框架思想都一致,都是統一管理組件state變化狀況,達到數據可控目的。本人使用了Redux,建議要會其中一種。對於EventEmitter或PostalJS這類的第三方庫是不建議使用的,這類全局事件框架並無統一管理組件數據變化,用多了會致使數據流不可控。

這裏就不細說,請選擇其中一種類庫,深刻學習下。

總結

簡單的組件交流咱們可使用上面非全局事件的簡單方式,可是當項目複雜,組件間層次愈來愈深,上面的交流方式就不太合適(固然仍是要用到的,簡單的交流)。強烈建議使用Flux、Relay、Redux、trandux等類庫其中一種,這些類庫不僅適合React,像Angular等均可以使用。

參考文章

相關文章
相關標籤/搜索