精益 React 學習指南 (Lean React)- 1.5 React 與 DOM

書籍完整目錄javascript

1.5 React 與 DOM

圖片描述

在這一節中,主要的討論範圍爲 React 與 DOM 相關的處理,包括:html

  1. 如何獲取 DOM 元素前端

  2. 如何作事件響應處理java

  3. 表單處理react

  4. style 屬性git

這節講述事後,咱們將會爲 TODO 應用添加完整的事件響應,包括新增,刪除,標記完成等。github

1.5.1 獲取 DOM 元素

上一節咱們已經講過組件的生命週期,DOM 真正被添加到 HTML 中的 hook 爲web

  • componentDidMountsegmentfault

  • componentDidUpdate數組

在這兩個 hook 函數中, 咱們能夠獲取真正的 DOM 元素,React 提供的獲取方法兩種方式

findDOMNode()

經過 ReactDOM 提供的 findDOMNode 方法, 傳入參數我組件實例,eg

var MyComponent = React.createClass({
    render: function() {
        return <div> .... </div>
    },
    componentDidMount: function() {
        var $root = ReactDOM.findDOMNode(this);
        console.log($root);
    }
})

須要注意的是此方法不能應用到無狀態組件上

Refs

上面的方法只能獲取到 root 元素,那若是個人 DOM 有不少層級,我想獲取一個子級的元素呢?
React 提供了 ref 屬性來實現這種需求。

每一個組件實例都有一個 this.refs 屬性,會自動引用全部包含 ref 屬性組件的 DOM, eg:

var MyComponent = React.createClass({
    render: function() {
        return  <div>
                    <button ref="btn">...</button>
                    <a href="" ref="link"></a>
                </div>
    },
    componentDidMount: function() {
        var $btn = this.refs.btn;
        var $link = this.refs.link;
        console.log($btn, $link);
    }
})

1.5.2 DOM 事件

官方事件文檔 http://facebook.github.io/react/docs/events.html

綁定事件

在 React 中綁定事件的方式很簡單,只須要在元素中添加事件名稱的屬性已經對應的處理函數,如:

var MyComponent = React.creatClass({
    render: function() {
        return  <div>
                    <button onClick={this.onClick}>Click Me</button>
                </div>
    },
    onClick: function() {
        console.log('click me');
    }
});

事件名稱和其餘屬性名稱同樣,服從駝峯式命名。

合成事件(SyntheticEvent)

在 React 中, 事件的處理由其內部本身實現的事件系統完成,觸發的事件都叫作 合成事件(SyntheticEvent),事件系統對瀏覽器作了兼容,其提供的 API 與原生的事件無異。

boolean bubbles
boolean cancelable
DOMEventTarget currentTarget
boolean defaultPrevented
number eventPhase
boolean isTrusted
DOMEvent nativeEvent
void preventDefault()
boolean isDefaultPrevented()
void stopPropagation()
boolean isPropagationStopped()
DOMEventTarget target
number timeStamp
string type

和原生事件的區別在於,事件不能異步話,如:

function onClick(event) {
  console.log(event); // => nullified object.
  console.log(event.type); // => "click"
  var eventType = event.type; // => "click"

  setTimeout(function() {
    console.log(event.type); // => null
    console.log(eventType); // => "click"
  }, 0);

  this.setState({clickEvent: event}); // Won't work. this.state.clickEvent will only contain null values.
  this.setState({eventType: event.type}); // You can still export event properties.
}

緣由是在事件系統的內部實現當中, 一個事件對象可能會被重用(也就是事件作了池化 Pooling)。當一個事件響應函數執行事後,事件的屬性被設置爲 null, 若是想用保持事件的值的話,能夠調用

event.persist()

這樣,屬性會被保留,而且事件也會被從池中取出。

事件捕獲和冒泡

在 DOM2.0 事件分爲捕獲階段和冒泡階段,React 中一般咱們註冊的事件爲冒泡事件,若是要註冊捕獲階段的事件,能夠在事件名稱後加 Capture 如:

onClick
onClickCapture

支持事件列表

粘貼板事件 {
    事件名稱:onCopy onCut onPaste
    屬性:DOMDataTransfer clipboardData
}

編輯事件 {
    事件名稱:onCompositionEnd onCompositionStart onCompositionUpdate
    屬性:string data
}

鍵盤事件 {
    事件名稱:onKeyDown onKeyPress onKeyUp
    屬性: {
        boolean altKey
        number charCode
        boolean ctrlKey
        boolean getModifierState(key)
        string key
        number keyCode
        string locale
        number location
        boolean metaKey
        boolean repeat
        boolean shiftKey
        number which
    }
}

// 焦點事件除了表單元素之外,能夠應用到全部元素中
焦點事件 {
    名稱:onFocus onBlur
    屬性:DOMEventTarget relatedTarget
}

表單事件 {
    名稱:onChange onInput onSubmit
}

鼠標事件 {
    名稱:{
        onClick onContextMenu onDoubleClick onDrag onDragEnd onDragEnter onDragExit onDragLeave onDragOver onDragStart onDrop onMouseDown onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver onMouseUp
    }
    屬性:{
        boolean altKey
        number button
        number buttons
        number clientX
        number clientY
        boolean ctrlKey
        boolean getModifierState(key)
        boolean metaKey
        number pageX
        number pageY
        DOMEventTarget relatedTarget
        number screenX
        number screenY
        boolean shiftKey
    }
}

選擇事件 {
    名稱:onSelect
}

觸摸事件 {
    名稱:onTouchCancel onTouchEnd onTouchMove onTouchStart
    屬性:{
        boolean altKey
        DOMTouchList changedTouches
        boolean ctrlKey
        boolean getModifierState(key)
        boolean metaKey
        boolean shiftKey
        DOMTouchList targetTouches
        DOMTouchList touches
    }
}

UI 事件 {
    名稱:onScroll
    屬性:{
        number detail
        DOMAbstractView view
    }
}

滾輪事件 {
    名稱:onWheel
    屬性:{
        number deltaMode
        number deltaX
        number deltaY
        number deltaZ
    }
}

媒體事件 {
    名稱:{
        onAbort onCanPlay onCanPlayThrough onDurationChange onEmptied onEncrypted onEnded onError onLoadedData onLoadedMetadata onLoadStart onPause onPlay onPlaying onProgress onRateChange onSeeked onSeeking onStalled onSuspend onTimeUpdate onVolumeChange onWaiting
    }
}

圖像事件 {
    名稱:onLoad onError
}

動畫事件 {
    名稱:onAnimationStart onAnimationEnd onAnimationIteration
    屬性:{
        string animationName
        string pseudoElement
        float elapsedTime
    }
}

漸變事件 {
    名稱:onTransitionEnd
    屬性: {
        string propertyName
        string pseudoElement
        float elapsedTime
    }
}

1.5.3 表單事件

在 React 中比較特殊的事件是表單事件,大多數組件都是經過屬性和狀態來決定的,可是表單組件如 input, select, option 這些組件的狀態用戶能夠修改,在 React 中會特殊處理這些組件的事件。

onChange 事件

和普通 HTML 中的 onChange 事件不一樣, 在原生組件中,只有 input 元素失去焦點纔會觸發 onChange 事件, 在 React 中,只要元素的值被修改就會觸發 onChange 事件。

var MyComponent = React.createClass({
    getInitialState: function() {
        return {
            value: ''
        }
    },
    render: function() {
        return  <div onChange={this.onChangeBubble}>
                    <input value={this.state.value} onChange={this.onChange}/>
                </div>
    },
    onChange: function(ev) {
        console.log('change: ' + ev.target.value);
        this.setState({
            value: ev.target.value
        });
    },
    // onChange 事件支持全部組件,能夠被用於監聽冒泡事件
    onChangeBubble: function(ev) {
        console.log('bubble onChange event', + ev.target.value);
    }
})

交互屬性

表單組件中能被用戶修改的屬性叫交互屬性,包括:

  1. value => <input><select> 組件

  2. checked => <input type="checkbox|radio">

  3. selected => <opiton>

textara

在 HTML 中,textarea 的值是像以下定義的:

<textarea name="" id="" cols="30" rows="10">
    some value
</textarea>

而在 React 中, TextArea 的使用方式同 input 組件,使用 value 來設置值

var MyComponent = function() {
    render: function() {
        return <div>
                    <textarea value={...} onChange={...}/>
                </div>
    }
}

select 組件

在 React 中 select 組件支持 value 值,value 值還支持多選

<select value="B">
    <option value="A">Apple</option>
    <option value="B">Banana</option>
    <option value="C">Cranberry</option>
  </select>

  <select multiple={true} value={['B', 'C']}>
    <option value="A">Apple</option>
    <option value="B">Banana</option>
    <option value="C">Cranberry</option>
  </select>

受控組件

在 React 中表單組件可分爲兩類,受控與非受控組件,受控組件是包含了 value 值的,如:

render: function() {
    return <input type="text" value="....."/>
}

爲何叫受控組件? 由於這個時候用戶不能修改 input 的值, input 的值永遠是 value 固定了的值。
若是去掉 value 屬性,那麼就能夠輸入值了。

那如何修改受控組件的值呢? 如上面的例子中, 添加 onChange 事件,事件內修改 value 屬性,value 屬性的值會被設置到組件的 value 中。

非受控組件

對應受控組件,也有非受控組件,那麼那種是非受控組價呢?

沒有 value 值的 input

render: function() {
    return <input type="text"/>
}

那若是想設置默認值呢?

能夠經過 defaultValue 屬性來設置

render: function() {
    return <input type="text" defaultValue="Default Value">
}

相似的對於 checkbox 有 defaultChecked 屬性

須要注意的是,默認值只適用於第一次渲染,在重渲染階段將不會適用。

checkbox 和 radio

checkbox 和 radio 比較特殊, 若是在 onChange 事件中調用了 preventDefault ,那麼瀏覽器不會更新 checked 狀態,即使事實上組件的值已經 checked 或者 unchecked 了 。

eg:

var CheckBox = React.createClass({
    getInitialState: function(){
        return {
            checked: false
        }
    },
    render: function() {
        return  <div>
            <input type="checkbox" 
                checked={this.state.checked} 
                onChange={this.onChange}/>
        </div>
    },
    onChange: function(ev) {
        this.setState({
            checked: true
        });
        ev.preventDefault();
    }
})

這個例子裏邊,checked 雖然更新爲 true ,可是 input 的值 checked 爲 false

那應如何處理 checkbox 呢?

  1. 避免調用 ev.preventDefault 就行

  2. 在 setTimeout 中處理 checked 的修改

  3. 使用 click 事件

1.5.4 style 屬性

在 React 中,能夠直接設置 style 屬性來控制樣式,不過與 HTML 不一樣的是, 傳入的 style 值爲一個對象, 對象的全部 key 都是駝峯式命名,eg:

render: function() {
    var style = {
        backgroundColor: 'red',
        height: 100,
        width: 100
    }
    return <div style={style}></div>
}

其中還能夠看到不一樣的地方時,爲了簡寫寬度高度值,能夠直接設置數字,對應 100 -> 100px。若是某些屬性不須要添加 px 後綴,React 也會自動去除。

經過屬性值駝峯式的緣由是 DOM 內部訪問 style 也是駝峯式。若是須要添加瀏覽器前綴瑞 -webkit--ms- 大駝峯(除了 ms ), 如:

var divStyle = {
  WebkitTransition: 'all', // 'W' 是大寫
  msTransition: 'all'      // 'ms' 爲小寫
};

爲何要用 inline 的樣式?

在之前的前端開發方式是 樣式結構和邏輯要分離, 而如今 React 中卻有不少人推崇 inline 的樣式。 在我看來因人而異,React 的這種模式也能作到樣式模塊化,樣式重用(借用 Js 的特色)。而且由於 React 的實現方式,Inline 樣式的性能甚至比 class 的方式高。

1.5.5 實例練習:完整功能的 TODO 應用

@todo

相關文章
相關標籤/搜索