編輯中。。。html
React 自身提供的 API 並很少,但總有一些比較 trick 的 API 和點是平時可能忽略的,本節將列舉一下相關的點。前端
setStatenode
setState function paramreact
setState 延遲數據庫
ref設計模式
ref as functionapi
區分 DOM node 和 React node數組
利用 ref 實現父組件到子組件的通訊性能優化
children前端框架
top api reactChildren
children as a function
dangerouslySetInnerHTML
狀態雙向綁定
受控組件
經過 value=null 轉變爲非受控
radio buttons
setState 的方法定義:
void setState( function|object nextState, [function callback] )
一般第一個參數爲 object nextState
,nextState 會被 merge 到組件的 state 數據當中,根據定義能夠看出第一個參數也能夠爲 function
,當爲函數時會得到兩個參數 previousState
、currentState
,其返回值當作 nextState 被 merge 到 state 中。
setState(function(previousState, currentProps) { return {myInteger: previousState.myInteger + 1}; });
首先介紹一下 transaction ,transaction 是數據庫操做的概念,將對數據的操做經過 transaction 來表示,將操做和執行分離,下面以一個僞代碼的方式表示對數據的操做步驟:
// 代碼只是爲了理解 transaction 和 react 無關 transactionManager = new TransactionManager transactionManager.add(new Transaction(function(){ // 修改數據的操做1 })) transactionManager.add(new Transaction(function(){ // 修改數據的操做2 })) transactionManager.perform()
能夠看到數據的修改操做和執行時分開的,React 和許多現代前端框架都借鑑了這一設計模式,將操做和執行分離 。 這樣的好處是能夠作到極大的性能優化,舉個例子,咱們知道 DOM 操做是極其耗時的,爲了優化性能,咱們能夠將 DOM 操做合併一塊兒執行。 React 的 DOM 更新也是合併執行的,這得益於 transaction 設計。
說了這麼多,爲的是理解 setState 的延時特性:setState 修改狀態事後並不會當即生效,而是會被 React 批量執行(batch update)。
eg:
// state = {a:1} this.setState({a:2}); console.log(this.state.a) // 1
注:transaction 在 React 中的設計和數據庫的 transaction 是不一樣的,但不礙於理解這裏的延遲批量執行策略,在 React 的實現章節中會詳解 React 的 transaction 。
由於延遲執行的特性,不能用上面的方法同步獲取最新狀態,爲了獲取 setState 成功事後的狀態,可使用 callback, callback 會在修改 state 並重渲染事後調用
setState(nextState, function()) { // do something after setState })
在某些狀況下,父組件可能須要引用子組件並調用其中的方法或找到子組件的 DOM,React 提供了 ref 屬性做爲實現方式,一般的作法是傳遞一個字符串,而後在組件中引用 refs
。
eg:
<input ref="myInput" />
var input = this.refs.myInput; var inputValue = input.value; var inputRect = input.getBoundingClientRect();
ref 也能夠做爲函數,函數傳遞的參數爲對應組件的實例 :
render: function() { return ( <TextInput ref={function(input) { if (input != null) { input.focus(); } }} /> ); },
React 官方更推薦 function , string 類型會在未來廢除。
須要注意的是,因組件不一樣 ref 獲取到的引用也不一樣,主要有兩種類型
自定義組件:ref 引用的是 React 組件實例;
原生組件: 如 div、input ,ref 引用的是 DOM node;
若是是自定義組件,爲了獲取組件的 DOM 可使用 ReactDOM.findDOMNode
方法:
const $dom = ReactDOM.findDOMNode(ref);
對於自定義組件的狀況,ref 能夠獲取子組件的實例,也就能夠訪問子組件的實例方法。 雖然這種通訊方式有違應用的 reactive 設計,但在實現第三方組件的時候,能夠用過這種方式實現一些 trick 的通訊。
eg:
const Child = React.createClass({ render() { return <div> ...</div> }, foo() { console.log('call child foo'); } }) const Parent = React.createClass({ render() { return (<div> <Child ref={child => {child.foo();}} /> </div>) } })
咱們已經知道了能夠經過 children 訪問傳遞給組件的子組件,分兩種狀況:
多個子組件:children 爲一個數組
單個子組件:children 爲單個組件
爲了更方便的操做 children, React 提供了一些工具函數
React.children.map(); React.children.forEach(); React.children.count(); // 返回惟一的一個 chid, 不然會報錯 React.children.only(); React.children.toArray();
Q:爲何不用 Array.prototype.map
, children 不是數組嗎?
A:children 只有一個 child 的狀況,做爲一個組件實例,並不是一個數組元素,除此以外還有一種叫 keyed fragment 的 children 類型,應當把 children 視爲 不透明對象
,操做它須要經過 children api。
爲了解釋 keyed fragment ,先引入一個問題:
若是定義了以下的這樣一個組件:
const Swapper = React.createClass({ propTypes: { // `leftChildren` and `rightChildren` can be a string, element, array, etc. leftChildren: React.PropTypes.node, rightChildren: React.PropTypes.node, swapped: React.PropTypes.bool }, render() { var children; if (this.props.swapped) { children = [this.props.rightChildren, this.props.leftChildren]; } else { children = [this.props.leftChildren, this.props.rightChildren]; } return <div>{children}</div>; } });
children 若是 rightChildren 和 leftChildren 都爲相同類型的數組,當 swapped 變量改變事後,左右會交換,出現的問題是全部的 children 都會 unmount 和 remount,由於對於 組件 set 並無辦法設置 key。
解決辦法有兩種, 第一種是左右 children 封裝在兩個不一樣的 wrapper div 中, 這樣由於每一個 child 都有 key , 因此不會致使 unmount。
第二種辦法是經過 keyed fragment,先來看看 keyed fragment 若是解決這個問題:
const createFragment = require('react-addons-create-fragment'); if (this.props.swapped) { children = createFragment({ right: this.props.rightChildren, left: this.props.leftChildren }); } else { children = createFragment({ left: this.props.leftChildren, right: this.props.rightChildren }); }
經過這種方式能夠爲 set 建立 key, createFragment 返回的 children 一樣能夠經過 React.children API 進行操做。
在將來 React 可能提供以下 API 來替換 createFragment
return ( <div> <x:frag key="right">{this.props.rightChildren}</x:frag>, <x:frag key="left">{this.props.leftChildren}</x:frag> </div> );
在某些狀況下,可能須要複製 React Element 並生成新的 Element 並附帶一些屬性,使用方式:
ReactElement cloneElement( ReactElement element, [object props], [children ...] )
對於使用場景上一節的 Decorator Pattern 就是最好的例子。
在上一節的 Presenter Pattern 中已經介紹過,能夠將傳遞一個函數做爲 children
更多相關討論可參考