React.js學習筆記之組件屬性與狀態

React.js學習筆記之組件屬性與狀態

@(前端技術)javascript

組件本質上是狀態機,輸入肯定,輸出必定肯定html

組件把狀態與結果一一對應起來,組件中有state與prop(狀態與屬性)。前端

  • 屬性是由父組件傳遞給子組件的java

  • 狀態是子組件內部維護的數據,當狀態發生變化的同時,組件也會進行更新。當狀態發生轉換時會觸發不一樣的鉤子函數,從而讓開發者有機會作出相應.react

statics

statics 對象容許你定義靜態的方法,這些靜態的方法能夠在組件類上調用。git

var MyComponent = React.createClass({
  statics: {
    customMethod: function(foo) {
      return foo === 'bar';
    }
  },
  render: function() {
  }
});

MyComponent.customMethod('bar');  // true

在這個塊兒裏面定義的方法都是靜態的,意味着你能夠在任何組件實例建立以前調用它們,這些方法不能獲取組件的 props 和 state。若是你想在靜態方法中檢查 props 的值,在調用處把 props 做爲參數傳入到靜態方法。github

props

this.props 表示一旦定義,就再也不改變的特性數組

屬性的用法

1.鍵值對

鍵 :值
值能夠有多種形式
<HelloWorld name= ? />服務器

  • 字符串:"XiaoWang"less

  • 求值表達式 {123}、{"XiaoWang"}

  • 數組{[1,2,3]}

  • 變量{variable}

  • 函數求值表達式{function}(不推薦,若是須要函數能夠單獨把函數提取出來而後單獨調用函數)

var HelloWorld =React.createClass({
    rencer:function () {
        return <p>Hello,{this.props.name ? this.props.name : "World"}</p>;
    },
});
var HelloUniverse = React.createClass({
    getInitialState:function () {
        return {name: ''};
    },
    handleChange: function (event) {
        this.setState({name: event.target.value});
    },
    render: function () {
        return <div>
        <HelloWorld name={this.state.name}></HelloWorld>
        <br/>
        <input type="text" onChange={this.handleChange} />
        </div>
    },
});
ReactDom.render(<HelloUniverse />,document.body);

2.展開語法{...props}

React會自動把對象中的屬性和值當作屬性的賦值

var HelloWorld =React.createClass({
    rencer:function () {
        return <p>Hello,{this.props.name1 + ' 'this.props.name2}</p>;
    },
});
var HelloUniverse = React.createClass({
    getInitialState:function () {
        return {
            name1:'Tim',
            name2:'John',
        };
    },
    handleChange: function (event) {
        this.setState({name: event.target.value});
    },
    render: function () {
        return <div>
        <HelloWorld name={...this.state}></HelloWorld>
        <br/>
        <input type="text" onChange={this.handleChange} />
        </div>
    },
});
ReactDom.render(<HelloUniverse />,document.body);

3.setProps

var HelloWorld =React.createClass({
    rencer:function () {
        return <p>Hello,{this.props.name ? this.props.name : "World"}</p>;
    },
});
var instance = React.render(<HelloWorld />,document.body);
instance.setProps({name:'Tim'});

setProps(object nextProps[, function callback])
能夠設置組件的屬性。這個方法已通過時了(與replaceProps等同樣),不久將被刪除。這個方法不支持ES6類組件React.Component擴展。

propTypes

組件的屬性能夠接受任意值,字符串、對象、函數等等均可以。有時,咱們須要一種機制,驗證別人使用組件時,提供的參數是否符合要求。

  • 組件類的PropTypes屬性,就是用來驗證組件實例的屬性是否符合要求

var MyTitle = React.createClass({
  propTypes: {
    title: React.PropTypes.string.isRequired,
  },
  render: function() {
     return <h1> {this.props.title} </h1>;
   }
});

上面的Mytitle組件有一個title屬性。PropTypes 告訴 React,這個 title 屬性是必須的,並且它的值必須是字符串。如今,咱們設置 title 屬性的值是一個數值。

var data = 123;
ReactDOM.render(
  <MyTitle title={data} />,
  document.body
);

這樣一來,title屬性就通不過驗證了。控制檯會顯示一行錯誤信息

getDefaultProps

getDefaultProps 方法能夠用來設置組件屬性的默認值。

var MyTitle = React.createClass({
  getDefaultProps : function () {
    return {
      title : 'Hello World'
    };
  },

  render: function() {
     return <h1> {this.props.title} </h1>;
   }
});
ReactDOM.render(<MyTitle />,document.body);

this.props.children

this.props 對象的屬性與組件的屬性一一對應,可是有一個例外,就是 this.props.children 屬性。它表示組件的全部子節點

var NotesList = React.createClass({
  render: function() {
    return (
      <ol>
      {
        React.Children.map(this.props.children, function (child) {
          return <li>{child}</li>;
        })
      }
      </ol>
    );
  }
});
ReactDOM.render(
  <NotesList>
    <span>hello</span>
    <span>world</span>
  </NotesList>,
  document.body
);

上面代碼的 NoteList 組件有兩個 span 子節點,它們均可以經過 this.props.children 讀取。
這裏須要注意, this.props.children 的值有三種可能:

  1. 若是當前組件沒有子節點,它就是 undefined;

  2. 若是有一個子節點,數據類型是 object;

  3. 若是有多個子節點,數據類型就是 array

React 提供一個工具方法 React.Children 來處理 this.props.children 。咱們能夠用 React.Children.map 來遍歷子節點,而不用擔憂 this.props.children 的數據類型是 undefined 仍是 object。
1.React.Children.map
object React.Children.map(object children, function fn [, object context])
在每個直接子級(包含在 children 參數中的)上調用 fn 函數,此函數中的 this 指向 上下文。若是 children 是一個內嵌的對象或者數組,它將被遍歷:不會傳入容器對象到 fn 中。若是 children 參數是 null 或者 undefined,那麼返回 null 或者 undefined 而不是一個空對象。
2.React.Children.forEach
React.Children.forEach(object children, function fn [, object context])
相似於 React.Children.map(),可是不返回對象。
3.React.Children.count
number React.Children.count(object children)
返回 children 當中的組件總數,和傳遞給 map 或者 forEach 的回調函數的調用次數一致。
4.React.Children.only
object React.Children.only(object children)
返回 children 中僅有的子級。不然拋出異常。

state

組件在運行時須要修改的數據就是狀態

組件免不了要與用戶互動,React 的一大創新,就是將組件當作是一個狀態機,一開始有一個初始狀態,而後用戶互動,致使狀態變化,從而觸發從新渲染 UI

this.state 是會隨着用戶互動而產生變化的特性。

state工做原理

經常使用的通知 React 數據變化的方法是調用 setState(data, callback)。這個方法會合並(merge) data 到 this.state,並從新渲染組件。渲染完成後,調用可選的 callback 回調。大部分狀況下不須要提供 callback,由於 React 會負責把界面更新到最新狀態。

var LikeButton = React.createClass({
  getInitialState: function() {
    return {liked: false};
  },
  handleClick: function(event) {
    this.setState({liked: !this.state.liked});
  },
  render: function() {
    var text = this.state.liked ? 'like' : 'haven\'t liked';
    return (
      <p onClick={this.handleClick}>
        You {text} this. Click to toggle.
      </p>
    );
  }
});

ReactDOM.render(
  <LikeButton />,
  document.getElementById('example')
);

getInitialState

object getInitialState()
getInitialState 方法用於定義初始狀態,也就是一個對象,這個對象能夠經過 this.state 屬性讀取。在組件掛載以前調用一次。返回值將會做爲 this.state 的初始值。

setState

setState(object nextState[, function callback])
合併 nextState 和當前 state。這是在事件處理函數中和請求回調函數中觸發 UI 更新的主要方法。另外,也支持可選的回調函數,該函數在 setState 執行完畢而且組件從新渲染完成以後調用。this.setState 方法用於修改狀態值,每次修改之後,自動調用 this.render 方法,再次渲染組件。

注意:

  1. 絕對不要直接改變 this.state,由於在以後調用 setState() 可能會替換掉你作的更改。把 this.state 當作不可變的。

  2. setState ( ) 不會馬上改變 this.state,而是建立一個即將處理的 state 轉變。在調用該方法以後獲取 this.state 的值可能會獲得現有的值,而不是最新設置的值。

  3. 不保證 setState ( ) 調用的同步性,爲了提高性能,可能會批量執行 state 轉變和 DOM 渲染。

  4. setState ( ) 將老是觸發一次重繪,除非在 shouldComponentUpdate ( ) 中實現了條件渲染邏輯。若是使用可變的對象,可是又不能在shouldComponentUpdate ( ) 中實現這種邏輯,僅在新 state 和以前的 state 存在差別的時候調用 setState ( ) 能夠避免沒必要要的從新渲染。

replaceState

replaceState(object nextState[, function callback])
相似於 setState(),可是刪除以前全部已存在的 state 鍵,這些鍵都不在 nextState 中。

注意:這個方法在ES6類組件擴展不可用,它可能會在將來某個React版本中刪除

哪些組件應該有 State?

大部分組件的工做應該是從 props 裏取數據並渲染出來。可是,有時須要對用戶輸入、服務器請求或者時間變化等做出響應,這時才須要使用 State。
嘗試把儘量多的組件無狀態化。這樣作能隔離 state,把它放到最合理的地方,也能減小冗餘,同時易於解釋程序運做過程。

經常使用的模式是建立多個只負責渲染數據的無狀態(stateless)組件,在它們的上層建立一個有狀態(stateful)組件並把它的狀態經過 props 傳給子級。這個有狀態的組件封裝了全部用戶的交互邏輯,而這些無狀態組件則負責聲明式地渲染數據。

哪些應該做爲 State?

State 應該包括那些可能被組件的事件處理器改變並觸發用戶界面更新的數據。 真實的應用中這種數據通常都很小且能被 JSON 序列化。當建立一個狀態化的組件時,想象一下表示它的狀態最少須要哪些數據,並只把這些數據存入 this.state。在 render() 裏再根據 state 來計算你須要的其它數據。你會發現以這種方式思考和開發程序最終每每是正確的,由於若是在 state 裏添加冗餘數據或計算所得數據,須要你常常手動保持數據同步,不能讓 React 來幫你處理。

哪些不該該做爲 State?

this.state 應該僅包括能表示用戶界面狀態所需的最少數據。所以,它不該該包括:

  • 計算所得數據: 不要擔憂根據 state 來預先計算數據 —— 把全部的計算都放到 render() 裏更容易保證用戶界面和數據的一致性。例如,在 state 裏有一個數組(listItems),咱們要把數組長度渲染成字符串, 直接在 render() 裏使用 this.state.listItems.length + ' list items' 比把它放到 state 裏好的多。

  • React 組件: 在 render() 裏使用當前 props 和 state 來建立它。

  • 基於 props 的重複數據: 儘量使用 props 來做爲唯一數據來源。把 props 保存到 state 的一個有效的場景是須要知道它之前值的時候,由於將來的 props 可能會變化。

屬性和狀態的對比

類似點

1. 都是純JS對象

純JS對象就是JS中的原生對象。是使用 { } 來建立的對象

2. 都會觸發render更新

狀態和屬性的變化都會觸發render更新,屬性和狀態的改變都會觸發整個生命週期流程,從處理屬性到是否應該更新,到進行對比,到最後的render真正執行,會觸發不少函數,咱們能夠在不一樣的函數中進行不一樣的對應操做。

3. 都具備肯定性

給定相同的屬性和相同的狀態,組件生成的都是相同的代碼

對比

Item 屬性 狀態
可否從父組件獲取初始值? o x
可否由父組件修改? o x
可否在組件內部設置默認值? o o
可否在組件內部修改? x o
可否設置子組件的初始值? o x
可否修改子組件的值? o x
  • 狀態只與組件自己相關,由本身自己維護。與父組件與子組件無關

  • 組件不能修改本身的屬性,但能夠從父組件獲取屬性,父組件也能修改其屬性,組件也能夠修改子組件的屬性

實例

關於屬性與狀態的實例代碼傳送門:React簡易小demo

小結

本文主要介紹了組件的屬性與狀態。組件化是React的主要思想,也是其核心所在。組件化也是前端將來的發展趨勢,React算是引領了這一潮流吧。關於屬性與狀態的更多用法在生命週期與協同使用中還會介紹。

PS

被官微轉發了有點小意外。聲明下,目前在網上找不到一個比較完整的文字react學習文章,博主決定利用業餘時間總結下。本博文原創內容很少,大部分都是對文檔與網上文章的本身學習總結。

特別感謝

相關文章
相關標籤/搜索