精益 React 學習指南 (Lean React)- 4.3 React Tricks

react tricks

編輯中。。。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

setState(function)

setState 的方法定義:

void setState(
      function|object nextState,
      [function callback]
    )

一般第一個參數爲 object nextState ,nextState 會被 merge 到組件的 state 數據當中,根據定義能夠看出第一個參數也能夠爲 function,當爲函數時會得到兩個參數 previousStatecurrentState ,其返回值當作 nextState 被 merge 到 state 中。

setState(function(previousState, currentProps) {
  return {myInteger: previousState.myInteger + 1};
});

setState 延遲

首先介紹一下 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  
})

ref

ref as function

在某些狀況下,父組件可能須要引用子組件並調用其中的方法或找到子組件的 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 類型會在未來廢除。

區分 DOM node 和 React node

須要注意的是,因組件不一樣 ref 獲取到的引用也不一樣,主要有兩種類型

  1. 自定義組件:ref 引用的是 React 組件實例;

  2. 原生組件: 如 div、input ,ref 引用的是 DOM node;

若是是自定義組件,爲了獲取組件的 DOM 可使用 ReactDOM.findDOMNode 方法:

const $dom = ReactDOM.findDOMNode(ref);

利用 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 top api

咱們已經知道了能夠經過 children 訪問傳遞給組件的子組件,分兩種狀況:

  1. 多個子組件:children 爲一個數組

  2. 單個子組件: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

爲了解釋 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>
);

clone Element

在某些狀況下,可能須要複製 React Element 並生成新的 Element 並附帶一些屬性,使用方式:

ReactElement cloneElement(
  ReactElement element,
  [object props],
  [children ...]
)

對於使用場景上一節的 Decorator Pattern 就是最好的例子。

function as children

在上一節的 Presenter Pattern 中已經介紹過,能夠將傳遞一個函數做爲 children
更多相關討論可參考

https://discuss.reactjs.org/t...

相關文章
相關標籤/搜索