React文檔精讀(上篇)

  • pre-notify
  • React中的element是什麼
    • 條件渲染
  • element和component
  • 渲染React Element
  • component與pure function
  • 函數式組件和類組件
  • state/狀態
    • setState
      • state設置更新時的自動合併機制
      • setState是異步的
  • component和事件
    • 和原生事件的區別
    • this
  • 表單
    • state和受控組件
      • value = {null}
      • 關於select
      • 關於file
    • ref和非受控組件
  • 列表和keys
    • key和重繪
    • li 和
    • key值沒法獲取
    • 不推薦用索引做爲key

pre-notify

previously:JSX,瞭解一下?javascript

(づ ̄ 3 ̄)づ此文會根據文檔的變更而不斷更新html

React中的element是什麼

上回咱們解釋了什麼是JSXJSX是React中對Javascript語法的延伸,它容許咱們使用JSX tag來構建一個虛擬DOM做爲真實DOM的小型描述文檔(它自己是一個Javascript對象)。java

其中,一個JSX tag就是一個React Elementreact

條件渲染

咱們說過JSX是能參與js流程控制的,So咱們能經過if一類的來決定一個React Element何時進行渲染何時不要。es6

let element =  (
    <div>
      {isLogin}&&
      <h2>hello {userName}</h2>
    </div>
  );
複製代碼

也支持三元數組

element和component

上面咱們已經知道一個JSX tag就是一個React Element,但一個JSX tag也能夠是一個React Componentbash

它長得像這樣babel

let elements = <Component10086 />
複製代碼

這。。。和以前的有撒區別?dom

首先咱們須要注意一個表明Component/組件的JSX tag必須首字母大寫,不然它就會把它當作一個普通的element去渲染。異步

嗯。。。很天然咱們會提出一個疑問,那當作普通的element去渲染和當作一個組件去渲染有什麼區別呢?

其實,React中的組件就像是Javascript 中的函數,能夠接收任意的輸入(咱們稱之爲props)而且可以返回React elements,這和咱們直接獲得React Element的區別在於,咱們能在函數內部再對element進行一次封裝,作一些條件渲染,設置state什麼的。


So,咱們光建立一個JSX tag是不夠的,咱們還須要定義一個對應的函數

function Component10086(props){
  return <h1>Hello Component!</h1>;
}
複製代碼

這個函數有一個pros屬性,咱們能夠像調用函數同樣在建立一個JSX tag時傳遞參數給這個函數,像這樣

let elements = <Component10086 prop1='第一個參數' prop2='第二個參數' />

// --- --- ---

//也支持... 來傳遞多個屬性
let data = {
 prop1:'第一個參數'
 ,prop2:'第二個參數'
}
let elements = <Component10086 {...data}/>
複製代碼

上面的prop1prop2兩個鍵值對會被封裝成一個props對象做爲函數的參數傳入

function Component10086(props){
  return (
  <h1 className={props.prop2}/*當作屬性渲染*/>
  	Hello Component!
    {props.prop1} //當作children渲染
  </h1>;
  )
}
複製代碼

[important] 注意:在JSX那一篇中咱們已經說過JSX是javascript語法的延伸,這意味着一個JSX tag能夠被賦值給一個js變量,能參與for和if流程控制,能做爲函數傳參,可以被當作函數返回值。So組件傳參時也能將一個JSX tag作爲參數進行傳遞


渲染React Element

咱們已經知道如何構建一個虛擬DOM來描述真實的DOM

let element = <h1 className={class1}>hello React!</h1>
複製代碼

(以上通過babel編譯後,咱們能夠發現它實際上是調用React.createELement來建立一個javascript對象來描述真實dom,上回已詳細說過再也不贅述)

[danger] 引入(import)react庫時,React必須是首字母大寫,由於編譯後咱們調用createElement方法時就是首字母大寫的React

但怎麼將這個虛擬DOM轉換成真正的DOM渲染在頁面上呢?

這就是咱們React中react-dom庫所作的事情了。引入這個庫後,咱們能調用其中的render方法將對應的真實DOM渲染到指定的掛載點上。

import ReactDOM from 'react-dom';
...

ReactDOM.render(
    element
    ,document.geElementById('root')
)
複製代碼

值得注意的是若是這個React Element被從新渲染,react只會從新渲染這個element中發生改變的dom節點。(咱們說過一個JSX tag能夠有children)。


一樣的,咱們能使用一樣的方式對一個組件進行渲染,

ReactDOM.render(
    <Component10086 prop1='...' />
    ,document.geElementById('root')
)
複製代碼

那麼,render是怎樣區分渲染的是一個組件仍是一個普通的element呢?

咱們調用ReactDOM.render去渲染一個JSX tag時,

咱們首先會查看這個tag的首字母是不是大寫,若是是大寫就表明是一個component,那麼react就會把它當作一個組件去渲染,

它會首先將JSX tag作爲參數傳遞的屬性封裝成一個props對象,而後再將這個對象傳遞給組件函數

組件函數接收到參數對象後會進行一系列處理,最終返回處理完成後的React element

最終,render拿到element轉換成真正的DOM元素渲染到頁面上。

component與pure function

關於React Component,有一點咱們須要注意的是,全部的react組件都必須是一個純函數。

什麼是純函數呢?

Such functions are called 「pure」 because they do not attempt to change their inputs, and always return the same result for the same inputs.

React官方給出的便是是,相同的輸入會產生相同的輸出,而且咱們不能在函數內部更改傳遞過來的props

這意味着,props是靜止的,不可更改的,但一些交互性灰常強的UI組件的一些屬性是常常會變化的。

那怎麼解決這個問題呢?這就是後話了,後面講到React state就是用來解決這個問題的。

[important] 注意: 若是屬性是不參與render()的那麼它就不該該被設計成state

函數式組件和類組件

組件有兩種形式,函數式的和類形式的,

//function
function Welcome(props) {
    return <h1>Hello, {props.name}</h1>;
}

//class
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

ReactDOM.render(<Welcome name='ahhh' age='123'/>,document.getElementById('root'));
複製代碼

此時這兩種寫法是等價的,但!

class類的形式支持一些函數式組件不支持的功能,好比說state

state/狀態

首先state也是prop,但不一樣於普通的props

普通的props咱們能夠在JSX tag上傳參,react會自動幫咱們將這些參數打包後掛載到組件上(this.props)。

state須要咱們在組件對象上手動設置,

setState

首先咱們在組件中初始化一個組件實例的state

class Clock extends React.Component{
  constructor(props){
    super(props);
    // 在構造函數內部定義初始狀態
    this.state = {date:new Date()};
  }
複製代碼

若是這個state須要發生改變,咱們須要注意,咱們不能直接經過this.state.date = xxx 這樣的形式去改變state,這樣是不會觸發render()進行重繪的。

咱們須要經過 this.setState 方法(來自繼承的React.Component)來改變咱們原有的state

語法: setState(updater[, callback])注意此方法是支持回調的

this.setState({
  date:new Date()
})
複製代碼

另外關於setState有兩點須要額外注意

state設置更新時的自動合併機制

咱們可能在一個構造函數中初始化多個state鍵值對

...
this.state = {
    state1:'xx'
    state2:'yy'
}
..
複製代碼

React state更新時的自動合併機制容許咱們這樣去更新state

this.setState({
    state1:'aaa'
})
複製代碼

能夠發現咱們只須要在setState中填上咱們要更改的部分而不是整個state對象。

setState是異步的

咱們可能在一個方法中連續使用屢次setState,但因爲設置是異步的,咱們不能在第二次調用setState方法時拿到第一次調用setState所設置的值,它們的state都是基於最初的state的。

那麼這個問題如何解決呢?

其實setState還有第二種形式,使用回調函數而非對象的形式去更新state,像這樣

this.setState((prevState,props)=>({counter:prevState.counter + Math.random()}));
this.setState((prevState,props)=>({counter:prevState.counter + props.increment}))
複製代碼

值得一提的是這種寫法是 setState(updater[, callback])的語法糖形式,最終仍然會編譯成這樣執行。

component和事件

和原生事件的區別

  • React事件命名採用的是駝峯式而非原生中的小寫形式
  • 原生綁定時傳遞的是一個字符串,而react則是利用{}
//原生
<button onclick="activateLasers()">
  Activate Lasers
</button>

//React
<button onClick={activateLasers}>
  Activate Lasers
</button>
複製代碼
  • react中的ev對象是通過react封裝過的
  • 不支持return false
function ActionLink() {
  function handleClick(e) { //這個event不是原生的,而是本身封裝的,故不存在兼容問題
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}
複製代碼

this

若是是用類的形式定義的組件,咱們須要注意事件函數中this的指向問題。

在react中咱們不須要手動使用addEventListener對元素進行事件函數的綁定,只須要在JSX tag上像原生行內式綁定事件函數同樣註冊事件便可。

但這存在一個問題,react並不會幫咱們把回調中的this指向組件實例,甚至這個this也不是指向那個應該綁定的DOM元素

咱們是但願這個this指向組件實例的,這樣咱們能拿到掛載在這個組件實例上的state。而改變回調this指向的方式大概有三種。

  • 經過bind綁定事件處理函數
  • 經過箭頭函數包裝事件處理函數
  • 經過ES7初始值語法來建立事件處理函數

咱們推薦第三種,十分方便

handleClick = ()=>{
    this.setState(...);
}
複製代碼

表單

state和受控組件

正常來講當咱們在一個input輸入後會馬上獲得相應的顯示在界面上。

但React容許咱們在輸入後,先把輸入的信息hold住,進行一些處理後再顯示在界面上。

這是怎麼作到的呢?這就是經過咱們以前所說的React state

首先我要讓一個input的value等於一個state

<input type="text" onChange={this.handleChange.bind(this,'username')} value={this.state.username}/>
複製代碼

咱們知道一個state在react中只能經過調用setState纔會觸發render致使重繪UI,這意味着只要咱們不馬上調用setState,那麼input的值就不會馬上改變。

在上面的示例中咱們經過給input綁定一個事件,來對輸入進行處理

handleChange = (key,event)=>{
    let val = event.target.value;
    this.setState({[key]:val})
}
複製代碼

這樣咱們就完成了對錶單輸入的截獲,使其受到了咱們的控制,咱們將這樣的表單元素稱之爲受控組件。


有一點要注意咱們在上慄用到了es6的computed property語法

setState({[key]:val})
複製代碼

其中{[key]:val}就至關於let o={};o[key]=val,這種語法容許咱們在設置對象的鍵名時也能使用變量。

咱們之因此要在事件處理函數中傳遞一個key過去,是由於一張表裏可能有不少表單元素,每個都對應咱們組件中的一個state,而這個key就是用來區分他們的。

value = {null}

表示再也不是受控組件

關於select

<select>
  <option value="grapefruit">Grapefruit</option>
  <option value="lime">Lime</option>
  <option selected value="coconut">Coconut</option>
  <option value="mango">Mango</option>
</select>
複製代碼

不推薦以上,推薦在select裏寫value

<select value={this.state.value} onChange={this.handleChange}>
    <option value="grapefruit">Grapefruit</option>
    <option value="lime">Lime</option>
    <option value="coconut">Coconut</option>
    <option value="mango">Mango</option>
</select>
複製代碼

多選

<select multiple={true} value={['a', 'b']}>
複製代碼

關於file

input=file 是不會受到react控制的

ref和非受控組件

咱們在受控組件中能經過綁定事件處理函數的形式拿到組件中的表單元素。

可是像<input type="file">這種非受控組件,咱們若是還想要截獲它的值怎麼截獲到呢?

首先咱們不可能像其它表單元素同樣在它自個兒身上綁定事件處理函數,由於對於file,只有form表單提交時咱們才能拿到file的值,

so咱們只能在form上綁定事件處理函數,那怎麼拿到form中的file元素呢?

這就須要利用到React ref

<input type="text" ref="username" />

// 處理函數中
let username = this.refs.username.value
複製代碼

以上寫法ref=一個字符串已經被廢棄

如今推薦這麼寫

<input type="text" ref={input=>this.username=input} /> //input就是渲染出的真實dom
複製代碼

能夠發現,這裏的ref裏對應的是一個函數,此函數會在當此虛擬DOM轉成真實DOM並插入到頁面以後馬上調用,參數接收到的就是插入的真實dom

更新:16.3新特のReact.createRef(),ref={input=>this.username=input}仍可用

列表和keys

記得咱們說過,JSX tag能夠做爲函數返回值

So,咱們可以這樣渲染一個列表

let array = [1,2,3,4,5];
let lists = array.map((item,index)=><li key={index}>{item}</li>);

ReactDOM.render((
  <ul>
    {lists}
  </ul>
), document.getElementById('root'));
複製代碼

注意上慄中咱們給每一個li都綁定了一個key,這個key是數組中的索引位置,是獨一無二的。

key和重繪

若是咱們不給li綁定key,React會報一個警告,

Warning: Each child in an array or iterator should have a unique "key" prop.

但其實它已經自動幫咱們加上key了。

爲何react渲染列表的時須要一個key呢?

記得咱們上面說過react重繪時不會將整個React Element都重繪的嗎。

嗯。。。那它是怎麼作到的呢?就是利用這個key了,若是沒有這個key,它是沒法區別element中的tag誰是誰的。

li 和 <ListItem>

有些時候一個li內的內容過於複雜,咱們會將其封裝成一個組件

這個時候咱們推薦把key掛載這個li的組件上,而不是組件內部返回的li上

如下示例出資React文檔

unction NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Correct! Key should be specified inside the array.
<ListItem key={number.toString()}
          value={number} />

);
複製代碼

key值沒法獲取

若是咱們在組件上傳遞了一個key值,這個key值並不會被包裝進props對象

不推薦用索引做爲key

詳見 in-depth explanation on the negative impacts of using an index as a key.


參考:

--- ToBeContinue... ---

相關文章
相關標籤/搜索