REACT

一、React 教程

React 是一個用於構建用戶界面的 JAVASCRIPT 庫。

React 主要用於構建UI,很多人認爲 React 是 MVC 中的 V(視圖)。

React 起源於 Facebook 的內部項目,用來架設 Instagram 的網站,並於 2013 年 5 月開源。

React 擁有較高的性能,代碼邏輯非常簡單,越來越多的人已開始關注和使用它。


React 特點

  • 1.聲明式設計 −React採用聲明範式,可以輕鬆描述應用。

  • 2.高效 −React通過對DOM的模擬,最大限度地減少與DOM的交互。

  • 3.靈活 −React可以與已知的庫或框架很好地配合。

  • 4.JSX − JSX 是 JavaScript 語法的擴展。React 開發不一定使用 JSX ,但我們建議使用它。

  • 5.組件 − 通過 React 構建組件,使得代碼更加容易得到複用,能夠很好的應用在大項目的開發中。

  • 6.單向響應的數據流 − React 實現了單向響應的數據流,從而減少了重複代碼,這也是它爲什麼比傳統數據綁定更簡單。


閱讀本教程前,您需要了解的知識:

在開始學習 React 之前,您需要具備以下基礎知識:

  • HTML5
  • CSS
  • JavaScript
React 實例
<div id="example"></div>
<script type="text/babel">
  ReactDOM.render(
    <h1>Hello, world!</h1>,
    document.getElementById('example')
  );
</script>

二、REACT安裝

React腳手架搭建及創建React項目

三、React 元素渲染

元素是構成 React 應用的最小單位,它用於描述屏幕上輸出的內容。

const element = <h1>Hello, world!</h1>;

與瀏覽器的 DOM 元素不同,React 當中的元素事實上是普通的對象,React DOM 可以確保 瀏覽器 DOM 的數據內容與 React 元素保持一致。

將元素渲染到 DOM 中

首先我們在一個 HTML 頁面中添加一個 id="example" 的 <div>:

<div id="example"></div>

在此 div 中的所有內容都將由 React DOM 來管理,所以我們將其稱爲 "根" DOM 節點。

我們用 React 開發應用時一般只會定義一個根節點。但如果你是在一個已有的項目當中引入 React 的話,你可能會需要在不同的部分單獨定義 React 根節點。

要將React元素渲染到根DOM節點中,我們通過把它們都傳遞給 ReactDOM.render() 的方法來將其渲染到頁面上:

實例

const element = <h1>Hello, world!</h1>; ReactDOM.render( element, document.getElementById('example') );

 

更新元素渲染

React 元素都是不可變的。當元素被創建之後,你是無法改變其內容或屬性的。

目前更新界面的唯一辦法是創建一個新的元素,然後將它傳入 ReactDOM.render() 方法:

來看一下這個計時器的例子:

實例


function tick() {
  const element = (
    <div>
      <h1>Hello, world!</h1>
      <h2>現在是 {new Date().toLocaleTimeString()}.</h2>
    </div>
  );
  ReactDOM.render(
    element,
    document.getElementById('example')
  );
}
 
setInterval(tick, 1000);

以上實例通過 setInterval() 方法,每秒鐘調用一次 ReactDOM.render()。

我們可以將要展示的部分封裝起來,以下實例用一個函數來表示:

實例


function Clock(props) {
  return (
    <div>
      <h1>Hello, world!</h1>
      <h2>現在是 {props.date.toLocaleTimeString()}.</h2>
    </div>
  );
}
 
function tick() {
  ReactDOM.render(
    <Clock date={new Date()} />,
    document.getElementById('example')
  );
}
 
setInterval(tick, 1000);

 

除了函數外我們還可以創建一個 React.Component 的 ES6 類,該類封裝了要展示的元素,需要注意的是在 render() 方法中,需要使用 this.props 替換 props:

實例


class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>現在是 {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}
 
function tick() {
  ReactDOM.render(
    <Clock date={new Date()} />,
    document.getElementById('example')
  );
}
 
setInterval(tick, 1000);

React 只會更新必要的部分

值得注意的是 React DOM 首先會比較元素內容先後的不同,而在渲染過程中只會更新改變了的部分

三、React JSX

React 使用 JSX 來替代常規的 JavaScript。

JSX 是一個看起來很像 XML 的 JavaScript 語法擴展。

我們不需要一定使用 JSX,但它有以下優點:

  • JSX 執行更快,因爲它在編譯爲 JavaScript 代碼後進行了優化。
  • 它是類型安全的,在編譯過程中就能發現錯誤。
  • 使用 JSX 編寫模板更加簡單快速。

我們先看下以下代碼:

const element = <h1>Hello, world!</h1>;

這種看起來可能有些奇怪的標籤語法既不是字符串也不是 HTML。

它被稱爲 JSX, 一種 JavaScript 的語法擴展。 我們推薦在 React 中使用 JSX 來描述用戶界面。

JSX 是在 JavaScript 內部實現的。

我們知道元素是構成 React 應用的最小單位,JSX 就是用來聲明 React 當中的元素。

與瀏覽器的 DOM 元素不同,React 當中的元素事實上是普通的對象,React DOM 可以確保 瀏覽器 DOM 的數據內容與 React 元素保持一致。

要將 React 元素渲染到根 DOM 節點中,我們通過把它們都傳遞給 ReactDOM.render() 的方法來將其渲染到頁面上:

React 實例

var myDivElement = <div className="foo" />;
ReactDOM.render(myDivElement, document.getElementById('example'));

 

注意:

由於 JSX 就是 JavaScript,一些標識符像 classfor 不建議作爲 XML 屬性名。作爲替代,React DOM 使用 classNamehtmlFor 來做對應的屬性。


使用 JSX

JSX 看起來類似 HTML ,我們可以看下實例:

ReactDOM.render( <h1>Hello, world!</h1>, document.getElementById('example') );

我們可以在以上代碼中嵌套多個 HTML 標籤,需要使用一個 div 元素包裹它,實例中的 p 元素添加了自定義屬性 data-myattribute,添加自定義屬性需要使用 data- 前綴。

React 實例


ReactDOM.render(
    <h1>Hello, world!</h1>,
    document.getElementById('example')
);

 

獨立文件

你的 React JSX 代碼可以放在一個獨立文件上,例如我們創建一個 helloworld_react.js 文件,代碼如下:

ReactDOM.render( <h1>Hello, world!</h1>, document.getElementById('example') );

然後在 HTML 文件中引入該 JS 文件:

React 實例


<body>
  <div id="example"></div>
<script type="text/babel" src="helloworld_react.js"></script>
</body>

JavaScript 表達式

我們可以在 JSX 中使用 JavaScript 表達式。表達式寫在花括號 {} 中。實例如下:

React 實例


ReactDOM.render(
    <div>
      <h1>{1+1}</h1>
    </div>
    ,
    document.getElementById('example')
);

在 JSX 中不能使用 if else 語句,但可以使用 conditional (三元運算) 表達式來替代。以下實例中如果變量 i 等於 1 瀏覽器將輸出 true, 如果修改 i 的值,則會輸出 false.

React 實例


ReactDOM.render(
    <div>
      <h1>{i == 1 ? 'True!' : 'False'}</h1>
    </div>
    ,
    document.getElementById('example')
);

樣式

React 推薦使用內聯樣式。我們可以使用 camelCase 語法來設置內聯樣式. React 會在指定元素數字後自動添加 px 。以下實例演示了爲 h1 元素添加 myStyle 內聯樣式:

React 實例


var myStyle = {
    fontSize: 100,
    color: '#FF0000'
};
ReactDOM.render(
    <h1 style = {myStyle}>菜鳥教程</h1>,
    document.getElementById('example')
);

註釋

註釋需要寫在花括號中,實例如下:

React 實例


ReactDOM.render(
    <div>
    <h1>菜鳥教程</h1>
    {/*註釋...*/}
     </div>,
    document.getElementById('example')
);

數組

JSX 允許在模板中插入數組,數組會自動展開所有成員:

React 實例


var arr = [
  <h1>菜鳥教程</h1>,
  <h2>學的不僅是技術,更是夢想!</h2>,
];
ReactDOM.render(
  <div>{arr}</div>,
  document.getElementById('example')
);

四、React 組件

本章節我們將討論如何使用組件使得我們的應用更容易來管理。

接下來我們封裝一個輸出 "Hello World!" 的組件,組件名爲 HelloMessage:

React 實例


function HelloMessage(props) {
    return <h1>Hello World!</h1>;
}
 
const element = <HelloMessage />;
 
ReactDOM.render(
    element,
    document.getElementById('example')
);

 

實例解析:

1、我們可以使用函數定義了一個組件:

function HelloMessage(props) {
    return <h1>Hello World!</h1>;
}

你也可以使用 ES6 class 來定義一個組件:

class Welcome extends React.Component {
  render() {
    return <h1>Hello World!</h1>;
  }
}

2、const element = <HelloMessage /> 爲用戶自定義的組件。

注意,原生 HTML 元素名以小寫字母開頭,而自定義的 React 類名以大寫字母開頭,比如 HelloMessage 不能寫成 helloMessage。除此之外還需要注意組件類只能包含一個頂層標籤,否則也會報錯。

如果我們需要向組件傳遞參數,可以使用 this.props 對象,實例如下:

React 實例


function HelloMessage(props) {
    return <h1>Hello {props.name}!</h1>;
}
 
const element = <HelloMessage name="Runoob"/>;
 
ReactDOM.render(
    element,
    document.getElementById('example')
);

 

以上實例中 name 屬性通過 props.name 來獲取。

注意,在添加屬性時, class 屬性需要寫成 className ,for 屬性需要寫成 htmlFor ,這是因爲 class 和 for 是 JavaScript 的保留字。


複合組件

我們可以通過創建多個組件來合成一個組件,即把組件的不同功能點進行分離。

以下實例我們實現了輸出網站名字和網址的組件:

React 實例


function Name(props) {
    return <h1>網站名稱:{props.name}</h1>;
}
function Url(props) {
    return <h1>網站地址:{props.url}</h1>;
}
function Nickname(props) {
    return <h1>網站小名:{props.nickname}</h1>;
}
function App() {
    return (
    <div>
        <Name name="菜鳥教程" />
        <Url url="http://www.runoob.com" />
        <Nickname nickname="Runoob" />
    </div>
    );
}
 
ReactDOM.render(
     <App />,
    document.getElementById('example')
);

實例中 App 組件使用了 Name、Url 和 Nickname 組件來輸出對應的信息。

五、React State(狀態)

  React 把組件看成是一個狀態機(State Machines)。通過與用戶的交互,實現不同狀態,然後渲染 UI,讓用戶界面和數據保持一致。

React 裏,只需更新組件的 state,然後根據新的 state 重新渲染用戶界面(不要操作 DOM)。

以下實例創建一個名稱擴展爲 React.Component 的 ES6 類,在 render() 方法中使用 this.state 來修改當前的時間。

添加一個類構造函數來初始化狀態 this.state,類組件應始終使用 props 調用基礎構造函數。

React 實例

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }
 
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>現在是 {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}
 
ReactDOM.render(
  <Clock />,
  document.getElementById('example')
)

 

接下來,我們將使Clock設置自己的計時器並每秒更新一次。

將生命週期方法添加到類中

在具有許多組件的應用程序中,在銷燬時釋放組件所佔用的資源非常重要。

每當 Clock 組件第一次加載到 DOM 中的時候,我們都想生成定時器,這在 React 中被稱爲掛載

同樣,每當 Clock 生成的這個 DOM 被移除的時候,我們也會想要清除定時器,這在 React 中被稱爲卸載

我們可以在組件類上聲明特殊的方法,當組件掛載或卸載時,來運行一些代碼:

React 實例


class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }
 
  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }
 
  componentWillUnmount() {
    clearInterval(this.timerID);
  }
 
  tick() {
    this.setState({
      date: new Date()
    });
  }
 
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>現在是 {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}
 
ReactDOM.render(
  <Clock />,
  document.getElementById('example')
);

 

實例解析:

componentDidMount() 與 componentWillUnmount() 方法被稱作生命週期鉤子。

在組件輸出到 DOM 後會執行 componentDidMount() 鉤子,我們就可以在這個鉤子上設置一個定時器。

this.timerID 爲定時器的 ID,我們可以在 componentWillUnmount() 鉤子中卸載定時器。

代碼執行順序:

  1. <Clock /> 被傳遞給 ReactDOM.render() 時,React 調用 Clock 組件的構造函數。 由於 Clock 需要顯示當前時間,所以使用包含當前時間的對象來初始化 this.state 。 我們稍後會更新此狀態。

  2. React 然後調用 Clock 組件的 render() 方法。這是 React 瞭解屏幕上應該顯示什麼內容,然後 React 更新 DOM 以匹配 Clock 的渲染輸出。

  3. Clock 的輸出插入到 DOM 中時,React 調用 componentDidMount() 生命週期鉤子。 在其中,Clock 組件要求瀏覽器設置一個定時器,每秒鐘調用一次 tick()

  4. 瀏覽器每秒鐘調用 tick() 方法。 在其中,Clock 組件通過使用包含當前時間的對象調用 setState() 來調度UI更新。 通過調用 setState() ,React 知道狀態已經改變,並再次調用 render() 方法來確定屏幕上應當顯示什麼。 這一次,render() 方法中的 this.state.date 將不同,所以渲染輸出將包含更新的時間,並相應地更新 DOM。

  5. 一旦 Clock 組件被從 DOM 中移除,React 會調用 componentWillUnmount() 這個鉤子函數,定時器也就會被清除。

數據自頂向下流動

父組件或子組件都不能知道某個組件是有狀態還是無狀態,並且它們不應該關心某組件是被定義爲一個函數還是一個類。

這就是爲什麼狀態通常被稱爲局部或封裝。 除了擁有並設置它的組件外,其它組件不可訪問。

以下實例中 FormattedDate 組件將在其屬性中接收到 date 值,並且不知道它是來自 Clock 狀態、還是來自 Clock 的屬性、亦或手工輸入:

React 實例


function FormattedDate(props) {
  return <h2>現在是 {props.date.toLocaleTimeString()}.</h2>;
}
 
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }
 
  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }
 
  componentWillUnmount() {
    clearInterval(this.timerID);
  }
 
  tick() {
    this.setState({
      date: new Date()
    });
  }
 
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <FormattedDate date={this.state.date} />
      </div>
    );
  }
}
 
ReactDOM.render(
  <Clock />,
  document.getElementById('example')
);

這通常被稱爲自頂向下或單向數據流。 任何狀態始終由某些特定組件所有,並且從該狀態導出的任何數據或 UI 只能影響樹中下方的組件。

如果你想象一個組件樹作爲屬性的瀑布,每個組件的狀態就像一個額外的水源,它連接在一個任意點,但也流下來。

爲了表明所有組件都是真正隔離的,我們可以創建一個 App 組件,它渲染三個Clock:

React 實例


function FormattedDate(props) {
  return <h2>現在是 {props.date.toLocaleTimeString()}.</h2>;
}
 
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }
 
  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }
 
  componentWillUnmount() {
    clearInterval(this.timerID);
  }
 
  tick() {
    this.setState({
      date: new Date()
    });
  }
 
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <FormattedDate date={this.state.date} />
      </div>
    );
  }
}
 
function App() {
  return (
    <div>
      <Clock />
      <Clock />
      <Clock />
    </div>
  );
}
 
ReactDOM.render(<App />, document.getElementById('example'));

以上實例中每個 Clock 組件都建立了自己的定時器並且獨立更新。

在 React 應用程序中,組件是有狀態還是無狀態被認爲是可能隨時間而變化的組件的實現細節。

我們可以在有狀態組件中使用無狀態組件,也可以在無狀態組件中使用有狀態組件。

六、React Props

state 和 props 主要的區別在於 props 是不可變的,而 state 可以根據與用戶交互來改變。這就是爲什麼有些容器組件需要定義 state 來更新和修改數據。 而子組件只能通過 props 來傳遞數據。


使用 Props

以下實例演示瞭如何在組件中使用 props:

React 實例


function HelloMessage(props) {
    return <h1>Hello {props.name}!</h1>;
}
 
const element = <HelloMessage name="Runoob"/>;
 
ReactDOM.render(
    element,
    document.getElementById('example')
);

實例中 name 屬性通過 props.name 來獲取。


默認 Props

你可以通過組件類的 defaultProps 屬性爲 props 設置默認值,實例如下:

React 實例


class HelloMessage extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}
 
HelloMessage.defaultProps = {
  name: 'Runoob'
};
 
const element = <HelloMessage/>;
 
ReactDOM.render(
  element,
  document.getElementById('example')
);

State 和 Props

以下實例演示瞭如何在應用中組合使用 state 和 props 。我們可以在父組件中設置 state, 並通過在子組件上使用 props 將其傳遞到子組件上。在 render 函數中, 我們設置 name 和 site 來獲取父組件傳遞過來的數據。

React 實例


class WebSite extends React.Component {
  constructor() {
      super();
 
      this.state = {
        name: "菜鳥教程",
        site: "https://www.runoob.com"
      }
    }
  render() {
    return (
      <div>
        <Name name={this.state.name} />
        <Link site={this.state.site} />
      </div>
    );
  }
}
 
 
 
class Name extends React.Component {
  render() {
    return (
      <h1>{this.props.name}</h1>
    );
  }
}
 
class Link extends React.Component {
  render() {
    return (
      <a href={this.props.site}>
        {this.props.site}
      </a>
    );
  }
}
 
ReactDOM.render(
  <WebSite />,
  document.getElementById('example')
);

Props 驗證

React.PropTypes 在 React v15.5 版本後已經移到了 prop-types 庫。

<script src="https://cdn.bootcss.com/prop-types/15.6.1/prop-types.js"></script>

Props 驗證使用 propTypes,它可以保證我們的應用組件被正確使用,React.PropTypes 提供很多驗證器 (validator) 來驗證傳入數據是否有效。當向 props 傳入無效數據時,JavaScript 控制檯會拋出警告。

以下實例創建一個 Mytitle 組件,屬性 title 是必須的且是字符串,非字符串類型會自動轉換爲字符串 :

React 16.4 實例


var title = "菜鳥教程";
// var title = 123;
class MyTitle extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.title}</h1>
    );
  }
}
 
MyTitle.propTypes = {
  title: PropTypes.string
};
ReactDOM.render(
    <MyTitle title={title} />,
    document.getElementById('example')
);

React 15.4 實例


var title = "菜鳥教程";
// var title = 123;
var MyTitle = React.createClass({
  propTypes: {
    title: React.PropTypes.string.isRequired,
  },
 
  render: function() {
     return <h1> {this.props.title} </h1>;
   }
});
ReactDOM.render(
    <MyTitle title={title} />,
    document.getElementById('example')
);

 

更多驗證器說明如下:


MyComponent.propTypes = {
    // 可以聲明 prop 爲指定的 JS 基本數據類型,默認情況,這些數據是可選的
   optionalArray: React.PropTypes.array,
    optionalBool: React.PropTypes.bool,
    optionalFunc: React.PropTypes.func,
    optionalNumber: React.PropTypes.number,
    optionalObject: React.PropTypes.object,
    optionalString: React.PropTypes.string,
 
    // 可以被渲染的對象 numbers, strings, elements 或 array
    optionalNode: React.PropTypes.node,
 
    //  React 元素
    optionalElement: React.PropTypes.element,
 
    // 用 JS 的 instanceof 操作符聲明 prop 爲類的實例。
    optionalMessage: React.PropTypes.instanceOf(Message),
 
    // 用 enum 來限制 prop 只接受指定的值。
    optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
 
    // 可以是多個對象類型中的一個
    optionalUnion: React.PropTypes.oneOfType([
      React.PropTypes.string,
      React.PropTypes.number,
      React.PropTypes.instanceOf(Message)
    ]),
 
    // 指定類型組成的數組
    optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
 
    // 指定類型的屬性構成的對象
    optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
 
    // 特定 shape 參數的對象
    optionalObjectWithShape: React.PropTypes.shape({
      color: React.PropTypes.string,
      fontSize: React.PropTypes.number
    }),
 
    // 任意類型加上 `isRequired` 來使 prop 不可空。
    requiredFunc: React.PropTypes.func.isRequired,
 
    // 不可空的任意類型
    requiredAny: React.PropTypes.any.isRequired,
 
    // 自定義驗證器。如果驗證失敗需要返回一個 Error 對象。不要直接使用 `console.warn` 或拋異常,因爲這樣 `oneOfType` 會失效。
    customProp: function(props, propName, componentName) {
      if (!/matchme/.test(props[propName])) {
        return new Error('Validation failed!');
      }
    }
  }
}

七、React 事件處理

React 元素的事件處理和 DOM 元素類似。但是有一點語法上的不同:

  • React 事件綁定屬性的命名採用駝峯式寫法,而不是小寫。
  • 如果採用 JSX 的語法你需要傳入一個函數作爲事件處理函數,而不是一個字符串(DOM 元素的寫法)

HTML 通常寫法是:

<button οnclick="activateLasers()">
  激活按鈕
</button>

React 中寫法爲:

<button onClick={activateLasers}>
  激活按鈕
</button>

在 React 中另一個不同是你不能使用返回 false 的方式阻止默認行爲, 你必須明確的使用 preventDefault。

例如,通常我們在 HTML 中阻止鏈接默認打開一個新頁面,可以這樣寫:

<a href="#" οnclick="console.log('點擊鏈接'); return false">
  點我
</a>

在 React 的寫法爲:


function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('鏈接被點擊');
  }
 
  return (
    <a href="#" onClick={handleClick}>
      點我
    </a>
  );
}

實例中 e 是一個合成事件。

使用 React 的時候通常你不需要使用 addEventListener 爲一個已創建的 DOM 元素添加監聽器。你僅僅需要在這個元素初始渲染的時候提供一個監聽器。

當你使用 ES6 class 語法來定義一個組件的時候,事件處理器會成爲類的一個方法。例如,下面的 Toggle 組件渲染一個讓用戶切換開關狀態的按鈕:

實例


class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};
 
    // 這邊綁定是必要的,這樣 `this` 才能在回調函數中使用
    this.handleClick = this.handleClick.bind(this);
  }
 
  handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }
 
  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}
 
ReactDOM.render(
  <Toggle />,
  document.getElementById('example')
);

 

你必須謹慎對待 JSX 回調函數中的 this,類的方法默認是不會綁定 this 的。如果你忘記綁定 this.handleClick 並把它傳入 onClick, 當你調用這個函數的時候 this 的值會是 undefined。

這並不是 React 的特殊行爲;它是函數如何在 JavaScript 中運行的一部分。通常情況下,如果你沒有在方法後面添加 () ,例如 onClick={this.handleClick},你應該爲這個方法綁定 this。

如果使用 bind 讓你很煩,這裏有兩種方式可以解決。如果你正在使用實驗性的屬性初始化器語法,你可以使用屬性初始化器來正確的綁定回調函數:


class LoggingButton extends React.Component {
  // 這個語法確保了 `this` 綁定在  handleClick 中
  // 這裏只是一個測試
  handleClick = () => {
    console.log('this is:', this);
  }
 
  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
  }
}

如果你沒有使用屬性初始化器語法,你可以在回調函數中使用 箭頭函數:


class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }
 
  render() {
    //  這個語法確保了 `this` 綁定在  handleClick 中
    return (
      <button onClick={(e) => this.handleClick(e)}>
        Click me
      </button>
    );
  }
}

使用這個語法有個問題就是每次 LoggingButton 渲染的時候都會創建一個不同的回調函數。在大多數情況下,這沒有問題。然而如果這個回調函數作爲一個屬性值傳入低階組件,這些組件可能會進行額外的重新渲染。我們通常建議在構造函數中綁定或使用屬性初始化器語法來避免這類性能問題。


向事件處理程序傳遞參數

通常我們會爲事件處理程序傳遞額外的參數。例如,若是 id 是你要刪除那一行的 id,以下兩種方式都可以向事件處理程序傳遞參數:

<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

上述兩種方式是等價的。

上面兩個例子中,參數 e 作爲 React 事件對象將會被作爲第二個參數進行傳遞。通過箭頭函數的方式,事件對象必須顯式的進行傳遞,但是通過 bind 的方式,事件對象以及更多的參數將會被隱式的進行傳遞。

值得注意的是,通過 bind 方式向監聽函數傳參,在類組件中定義的監聽函數,事件對象 e 要排在所傳遞參數的後面,例如:


class Popper extends React.Component{
    constructor(){
        super();
        this.state = {name:'Hello world!'};
    }
    
    preventPop(name, e){    //事件對象e要放在最後
        e.preventDefault();
        alert(name);
    }
    
    render(){
        return (
            <div>
                <p>hello</p>
                {/* 通過 bind() 方法傳遞參數。 */}
                <a href="https://reactjs.org" onClick={this.preventPop.bind(this,this.state.name)}>Click</a>
            </div>
        );
    }
}

九、React 條件渲染

在 React 中,你可以創建不同的組件來封裝各種你需要的行爲。然後還可以根據應用的狀態變化只渲染其中的一部分。

React 中的條件渲染和 JavaScript 中的一致,使用 JavaScript 操作符 if 或條件運算符來創建表示當前狀態的元素,然後讓 React 根據它們來更新 UI。

先來看兩個組件:

function UserGreeting(props) {
  return <h1>歡迎回來!</h1>;
}

function GuestGreeting(props) {
  return <h1>請先註冊。</h1>;
}

我們將創建一個 Greeting 組件,它會根據用戶是否登錄來顯示其中之一:

React 實例


function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;
  if (isLoggedIn) {
    return <UserGreeting />;
  }
  return <GuestGreeting />;
}
 
ReactDOM.render(
  // 嘗試修改 isLoggedIn={true}:
  <Greeting isLoggedIn={false} />,
  document.getElementById('example')
);

元素變量

你可以使用變量來儲存元素。它可以幫助你有條件的渲染組件的一部分,而輸出的其他部分不會更改。

在下面的例子中,我們將要創建一個名爲 LoginControl 的有狀態的組件。

它會根據當前的狀態來渲染 <LoginButton /> 或 <LogoutButton />,它也將渲染前面例子中的 <Greeting />。

React 實例


class LoginControl extends React.Component {
  constructor(props) {
    super(props);
    this.handleLoginClick = this.handleLoginClick.bind(this);
    this.handleLogoutClick = this.handleLogoutClick.bind(this);
    this.state = {isLoggedIn: false};
  }
 
  handleLoginClick() {
    this.setState({isLoggedIn: true});
  }
 
  handleLogoutClick() {
    this.setState({isLoggedIn: false});
  }
 
  render() {
    const isLoggedIn = this.state.isLoggedIn;
 
    let button = null;
    if (isLoggedIn) {
      button = <LogoutButton onClick={this.handleLogoutClick} />;
    } else {
      button = <LoginButton onClick={this.handleLoginClick} />;
    }
 
    return (
      <div>
        <Greeting isLoggedIn={isLoggedIn} />
        {button}
      </div>
    );
  }
}
 
ReactDOM.render(
  <LoginControl />,
  document.getElementById('example')
);

與運算符 &&

你可以通過用花括號包裹代碼在 JSX 中嵌入任何表達式 ,也包括 JavaScript 的邏輯與 &&,它可以方便地條件渲染一個元素。

React 實例


function Mailbox(props) {
  const unreadMessages = props.unreadMessages;
  return (
    <div>
      <h1>Hello!</h1>
      {unreadMessages.length > 0 &&
        <h2>
          您有 {unreadMessages.length} 條未讀信息。
        </h2>
      }
    </div>
  );
}
 
const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
  <Mailbox unreadMessages={messages} />,
  document.getElementById('example')
);

 

在 JavaScript 中,true && expression 總是返回 expression,而 false && expression 總是返回 false

因此,如果條件是 true,&& 右側的元素就會被渲染,如果是 false,React 會忽略並跳過它。


三目運算符

條件渲染的另一種方法是使用 JavaScript 的條件運算符:

condition ? true : false。

在下面的例子中,我們用它來有條件的渲染一小段文本。
 

render() { const isLoggedIn = this.state.isLoggedIn; return (

The user is {isLoggedIn ? 'currently' : 'not'} logged in.

); } 

同樣它也可以用在較大的表達式中,雖然不太直觀:

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      {isLoggedIn ? (
        <LogoutButton onClick={this.handleLogoutClick} />
      ) : (
        <LoginButton onClick={this.handleLoginClick} />
      )}
    </div>
  );
}

阻止組件渲染

在極少數情況下,你可能希望隱藏組件,即使它被其他組件渲染。讓 render 方法返回 null 而不是它的渲染結果即可實現。

在下面的例子中,<WarningBanner /> 根據屬性 warn 的值條件渲染。如果 warn 的值是 false,則組件不會渲染:

React 實例


function WarningBanner(props) {
  if (!props.warn) {
    return null;
  }
 
  return (
    <div className="warning">
      警告!
    </div>
  );
}
 
class Page extends React.Component {
  constructor(props) {
    super(props);
    this.state = {showWarning: true}
    this.handleToggleClick = this.handleToggleClick.bind(this);
  }
 
  handleToggleClick() {
    this.setState(prevState => ({
      showWarning: !prevState.showWarning
    }));
  }
 
  render() {
    return (
      <div>
        <WarningBanner warn={this.state.showWarning} />
        <button onClick={this.handleToggleClick}>
          {this.state.showWarning ? '隱藏' : '顯示'}
        </button>
      </div>
    );
  }
}
 
ReactDOM.render(
  <Page />,
  document.getElementById('example')
);

組件的 render 方法返回 null 並不會影響該組件生命週期方法的回調。例如,componentWillUpdate 和 componentDidUpdate 依然可以被調用。

十、React 列表 & Keys

我們可以使用 JavaScript 的 map() 方法來創建列表。

React 實例

使用 map() 方法遍歷數組生成了一個 1 到 5 的數字列表:


const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((numbers) =>
  <li>{numbers}</li>
);
 
ReactDOM.render(
  <ul>{listItems}</ul>,
  document.getElementById('example')
);

我們可以將以上實例重構成一個組件,組件接收數組參數,每個列表元素分配一個 key,不然會出現警告 a key should be provided for list items,意思就是需要包含 key:

React 實例


function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li key={number.toString()}>
      {number}
    </li>
  );
  return (
    <ul>{listItems}</ul>
  );
}
 
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('example')
);

 


Keys

Keys 可以在 DOM 中的某些元素被增加或刪除的時候幫助 React 識別哪些元素髮生了變化。因此你應當給數組中的每一個元素賦予一個確定的標識。

const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  <li key={number.toString()}>
    {number}
  </li>
);

一個元素的 key 最好是這個元素在列表中擁有的一個獨一無二的字符串。通常,我們使用來自數據的 id 作爲元素的 key:

const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
);

當元素沒有確定的 id 時,你可以使用他的序列號索引 index 作爲 key:

const todoItems = todos.map((todo, index) =>
  // 只有在沒有確定的 id 時使用
  <li key={index}>
    {todo.text}
  </li>
);

如果列表可以重新排序,我們不建議使用索引來進行排序,因爲這會導致渲染變得很慢。


用keys提取組件

元素的 key 只有在它和它的兄弟節點對比時纔有意義。

比方說,如果你提取出一個 ListItem 組件,你應該把 key 保存在數組中的這個 <ListItem /> 元素上,而不是放在 ListItem 組件中的 <li> 元素上。

錯誤的示範

function ListItem(props) {
  const value = props.value;
  return (
    // 錯啦!你不需要在這裏指定key:
    <li key={value.toString()}>
      {value}
    </li>
  );
}

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    //錯啦!元素的key應該在這裏指定:
    <ListItem value={number} />
  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('example')
);

key的正確使用方式

React 實例


function Blog(props) {
  const sidebar = (
    <ul>
      {props.posts.map((post) =>
        <li key={post.id}>
          {post.title}
        </li>
      )}
    </ul>
  );
  const content = props.posts.map((post) =>
    <div key={post.id}>
      <h3>{post.title}</h3>
      <p>{post.content}</p>
    </div>
  );
  return (
    <div>
      {sidebar}
      <hr />
      {content}
    </div>
  );
}
 
const posts = [
  {id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
  {id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
ReactDOM.render(
  <Blog posts={posts} />,
  document.getElementById('example')
);

當你在 map() 方法的內部調用元素時,你最好隨時記得爲每一個元素加上一個獨一無二的 key。


元素的 key 在他的兄弟元素之間應該唯一

數組元素中使用的 key 在其兄弟之間應該是獨一無二的。然而,它們不需要是全局唯一的。當我們生成兩個不同的數組時,我們可以使用相同的鍵。

React 實例


function Blog(props) {
  const sidebar = (
    <ul>
      {props.posts.map((post) =>
        <li key={post.id}>
          {post.title}
        </li>
      )}
    </ul>
  );
  const content = props.posts.map((post) =>
    <div key={post.id}>
      <h3>{post.title}</h3>
      <p>{post.content}</p>
    </div>
  );
  return (
    <div>
      {sidebar}
      <hr />
      {content}
    </div>
  );
}
 
const posts = [
  {id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
  {id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
ReactDOM.render(
  <Blog posts={posts} />,
  document.getElementById('example')
);

key 會作爲給 React 的提示,但不會傳遞給你的組件。如果您的組件中需要使用和 key 相同的值,請將其作爲屬性傳遞:

const content = posts.map((post) =>
  <Post
    key={post.id}
    id={post.id}
    title={post.title} />
);

上面例子中,Post 組件可以讀出 props.id,但是不能讀出 props.key。


在 jsx 中嵌入 map()

在上面的例子中,我們聲明瞭一個單獨的 listItems 變量並將其包含在 JSX 中:

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <ListItem key={number.toString()}
              value={number} />

  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}

JSX 允許在大括號中嵌入任何表達式,所以我們可以在 map() 中這樣使用:

React 實例


function NumberList(props) {
  const numbers = props.numbers;
  return (
    <ul>
      {numbers.map((number) =>
        <ListItem key={number.toString()}
                  value={number} />
 
      )}
    </ul>
  );
}

這麼做有時可以使你的代碼更清晰,但有時這種風格也會被濫用。就像在 JavaScript 中一樣,何時需要爲了可讀性提取出一個變量,這完全取決於你。但請記住,如果一個 map() 嵌套了太多層級,那你就可以提取出組件。

十一、React 組件 API

在本章節中我們將討論 React 組件 API。我們將講解以下7個方法:

  • 設置狀態:setState
  • 替換狀態:replaceState
  • 設置屬性:setProps
  • 替換屬性:replaceProps
  • 強制更新:forceUpdate
  • 獲取DOM節點:findDOMNode
  • 判斷組件掛載狀態:isMounted

設置狀態:setState

setState(object nextState[, function callback])

參數說明

  • nextState,將要設置的新狀態,該狀態會和當前的state合併
  • callback,可選參數,回調函數。該函數會在setState設置成功,且組件重新渲染後調用。

合併nextState和當前state,並重新渲染組件。setState是React事件處理函數中和請求回調函數中觸發UI更新的主要方法。

關於setState

不能在組件內部通過this.state修改狀態,因爲該狀態會在調用setState()後被替換。

setState()並不會立即改變this.state,而是創建一個即將處理的state。setState()並不一定是同步的,爲了提升性能React會批量執行state和DOM渲染。

setState()總是會觸發一次組件重繪,除非在shouldComponentUpdate()中實現了一些條件渲染邏輯。

實例

React 實例


class Counter extends React.Component{
  constructor(props) {
      super(props);
      this.state = {clickCount: 0};
      this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick() {
    this.setState(function(state) {
      return {clickCount: state.clickCount + 1};
    });
  }
  render () {
    return (<h2 onClick={this.handleClick}>點我!點擊次數爲: {this.state.clickCount}</h2>);
  }
}
ReactDOM.render(
  <Counter />,
  document.getElementById('example')
);

 

實例中通過點擊 h2 標籤來使得點擊計數器加 1。


替換狀態:replaceState

replaceState(object nextState[, function callback])
  • nextState,將要設置的新狀態,該狀態會替換當前的state
  • callback,可選參數,回調函數。該函數會在replaceState設置成功,且組件重新渲染後調用。

replaceState()方法與setState()類似,但是方法只會保留nextState中狀態,原state不在nextState中的狀態都會被刪除。


設置屬性:setProps

setProps(object nextProps[, function callback])
  • nextProps,將要設置的新屬性,該狀態會和當前的props合併
  • callback,可選參數,回調函數。該函數會在setProps設置成功,且組件重新渲染後調用。

設置組件屬性,並重新渲染組件。

props相當於組件的數據流,它總是會從父組件向下傳遞至所有的子組件中。當和一個外部的JavaScript應用集成時,我們可能會需要向組件傳遞數據或通知React.render()組件需要重新渲染,可以使用setProps()

更新組件,我可以在節點上再次調用React.render(),也可以通過setProps()方法改變組件屬性,觸發組件重新渲染。


替換屬性:replaceProps

replaceProps(object nextProps[, function callback])
  • nextProps,將要設置的新屬性,該屬性會替換當前的props
  • callback,可選參數,回調函數。該函數會在replaceProps設置成功,且組件重新渲染後調用。

replaceProps()方法與setProps類似,但它會刪除原有 props。


強制更新:forceUpdate

forceUpdate([function callback])

參數說明

  • callback,可選參數,回調函數。該函數會在組件render()方法調用後調用。

forceUpdate()方法會使組件調用自身的render()方法重新渲染組件,組件的子組件也會調用自己的render()。但是,組件重新渲染時,依然會讀取this.props和this.state,如果狀態沒有改變,那麼React只會更新DOM。

forceUpdate()方法適用於this.props和this.state之外的組件重繪(如:修改了this.state後),通過該方法通知React需要調用render()

一般來說,應該儘量避免使用forceUpdate(),而僅從this.props和this.state中讀取狀態並由React觸發render()調用。


獲取DOM節點:findDOMNode

DOMElement findDOMNode()
  • 返回值:DOM元素DOMElement

如果組件已經掛載到DOM中,該方法返回對應的本地瀏覽器 DOM 元素。當render返回nullfalse時,this.findDOMNode()也會返回null。從DOM 中讀取值的時候,該方法很有用,如:獲取表單字段的值和做一些 DOM 操作。


判斷組件掛載狀態:isMounted

bool isMounted()
  • 返回值:truefalse,表示組件是否已掛載到DOM中

isMounted()方法用於判斷組件是否已掛載到DOM中。可以使用該方法保證了setState()forceUpdate()在異步場景下的調用不會出錯。

isMounted 的方法在 ES6 中已經廢除。主要的原因是它經過實際使用與測試可能不足以檢測組件是否掛載,尤其是對於有一些異步的程序情況,以及邏輯上造成混亂。現在用以下方法代替:

componentDidMount() {
    this.mounted = true;
}

componentWillUnmount() {
    this.mounted = false;
}

十二、React 組件生命週期

在本章節中我們將討論 React 組件的生命週期。

組件的生命週期可分成三個狀態:

  • Mounting:已插入真實 DOM
  • Updating:正在被重新渲染
  • Unmounting:已移出真實 DOM

生命週期的方法有:

  • componentWillMount 在渲染前調用,在客戶端也在服務端。

  • componentDidMount : 在第一次渲染後調用,只在客戶端。之後組件已經生成了對應的DOM結構,可以通過this.getDOMNode()來進行訪問。 如果你想和其他JavaScript框架一起使用,可以在這個方法中調用setTimeout, setInterval或者發送AJAX請求等操作(防止異步操作阻塞UI)。

  • componentWillReceiveProps 在組件接收到一個新的 prop (更新後)時被調用。這個方法在初始化render時不會被調用。

  • shouldComponentUpdate 返回一個布爾值。在組件接收到新的props或者state時被調用。在初始化時或者使用forceUpdate時不被調用。可以在你確認不需要更新組件時使用。

  • componentWillUpdate在組件接收到新的props或者state但還沒有render時被調用。在初始化時不會被調用。

  • componentDidUpdate 在組件完成更新後立即調用。在初始化時不會被調用。

  • componentWillUnmount在組件從 DOM 中移除之前立刻被調用。

這些方法的詳細說明,可以參考官方文檔

以下實例在 Hello 組件加載以後,通過 componentDidMount 方法設置一個定時器,每隔100毫秒重新設置組件的透明度,並重新渲染:

React 實例


class Hello extends React.Component {
 
  constructor(props) {
      super(props);
      this.state = {opacity: 1.0};
  }
 
  componentDidMount() {
    this.timer = setInterval(function () {
      var opacity = this.state.opacity;
      opacity -= .05;
      if (opacity < 0.1) {
        opacity = 1.0;
      }
      this.setState({
        opacity: opacity
      });
    }.bind(this), 100);
  }
 
  render () {
    return (
      <div style={{opacity: this.state.opacity}}>
        Hello {this.props.name}
      </div>
    );
  }
}
 
ReactDOM.render(
  <Hello name="world"/>,
  document.body
);

 

以下實例初始化 statesetNewnumber 用於更新 state。所有生命週期在 Content 組件中。

React 實例


class Button extends React.Component {
  constructor(props) {
      super(props);
      this.state = {data: 0};
      this.setNewNumber = this.setNewNumber.bind(this);
  }
  
  setNewNumber() {
    this.setState({data: this.state.data + 1})
  }
  render() {
      return (
         <div>
            <button onClick = {this.setNewNumber}>INCREMENT</button>
            <Content myNumber = {this.state.data}></Content>
         </div>
      );
    }
}
 
 
class Content extends React.Component {
  componentWillMount() {
      console.log('Component WILL MOUNT!')
  }
  componentDidMount() {
       console.log('Component DID MOUNT!')
  }
  componentWillReceiveProps(newProps) {
        console.log('Component WILL RECEIVE PROPS!')
  }
  shouldComponentUpdate(newProps, newState) {
        return true;
  }
  componentWillUpdate(nextProps, nextState) {
        console.log('Component WILL UPDATE!');
  }
  componentDidUpdate(prevProps, prevState) {
        console.log('Component DID UPDATE!')
  }
  componentWillUnmount() {
         console.log('Component WILL UNMOUNT!')
  }
 
    render() {
      return (
        <div>
          <h3>{this.props.myNumber}</h3>
        </div>
      );
    }
}
ReactDOM.render(
   <div>
      <Button />
   </div>,
  document.getElementById('example')
);

十三、React AJAX

React 組件的數據可以通過 componentDidMount 方法中的 Ajax 來獲取,當從服務端獲取數據時可以將數據存儲在 state 中,再用 this.setState 方法重新渲染 UI。

當使用異步加載數據時,在組件卸載前使用 componentWillUnmount 來取消未完成的請求。

以下實例演示了獲取 Github 用戶最新 gist 共享描述:

React 實例


class UserGist extends React.Component {
  constructor(props) {
      super(props);
      this.state = {username: '', lastGistUrl: ''};
  }
 
 
  componentDidMount() {
    this.serverRequest = $.get(this.props.source, function (result) {
      var lastGist = result[0];
      this.setState({
        username: lastGist.owner.login,
        lastGistUrl: lastGist.html_url
      });
    }.bind(this));
  }
 
  componentWillUnmount() {
    this.serverRequest.abort();
  }
 
  render() {
    return (
      <div>
        {this.state.username} 用戶最新的 Gist 共享地址:
        <a href={this.state.lastGistUrl}>{this.state.lastGistUrl}</a>
      </div>
    );
  }
}
 
ReactDOM.render(
  <UserGist source="https://api.github.com/users/octocat/gists" />,
  document.getElementById('example')
);

以上代碼使用 jQuery 完成 Ajax 請求。

十三、React 表單與事件

本章節我們將討論如何在 React 中使用表單。

HTML 表單元素與 React 中的其他 DOM 元素有所不同,因爲表單元素生來就保留一些內部狀態。

在 HTML 當中,像 <input>, <textarea>, 和 <select> 這類表單元素會維持自身狀態,並根據用戶輸入進行更新。但在React中,可變的狀態通常保存在組件的狀態屬性中,並且只能用 setState() 方法進行更新。

一個簡單的實例

在實例中我們設置了輸入框 input 值 value = {this.state.data}。在輸入框值發生變化時我們可以更新 state。我們可以使用 onChange 事件來監聽 input 的變化,並修改 state。

React 實例


class HelloMessage extends React.Component {
  constructor(props) {
      super(props);
      this.state = {value: 'Hello Runoob!'};
      this.handleChange = this.handleChange.bind(this);
  }
 
  handleChange(event) {
    this.setState({value: event.target.value});
  }
  render() {
    var value = this.state.value;
    return <div>
            <input type="text" value={value} onChange={this.handleChange} /> 
            <h4>{value}</h4>
           </div>;
  }
}
ReactDOM.render(
  <HelloMessage />,
  document.getElementById('example')
);

 

上面的代碼將渲染出一個值爲 Hello Runoob! 的 input 元素,並通過 onChange 事件響應更新用戶輸入的值。

實例 2

在以下實例中我們將爲大家演示如何在子組件上使用表單。 onChange 方法將觸發 state 的更新並將更新的值傳遞到子組件的輸入框的 value 上來重新渲染界面。

你需要在父組件通過創建事件句柄 (handleChange) ,並作爲 prop (updateStateProp) 傳遞到你的子組件上。

React 實例


class Content extends React.Component {
  render() {
    return  <div>
            <input type="text" value={this.props.myDataProp} onChange={this.props.updateStateProp} /> 
            <h4>{this.props.myDataProp}</h4>
            </div>;
  }
}
class HelloMessage extends React.Component {
  constructor(props) {
      super(props);
      this.state = {value: 'Hello Runoob!'};
      this.handleChange = this.handleChange.bind(this);
  }
 
  handleChange(event) {
    this.setState({value: event.target.value});
  }
  render() {
    var value = this.state.value;
    return <div>
            <Content myDataProp = {value} 
              updateStateProp = {this.handleChange}></Content>
           </div>;
  }
}
ReactDOM.render(
  <HelloMessage />,
  document.getElementById('example')
);

 

Select 下拉菜單

在 React 中,不使用 selected 屬性,而在根 select 標籤上用 value 屬性來表示選中項。

React 實例


class FlavorForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: 'coconut'};
 
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
 
  handleChange(event) {
    this.setState({value: event.target.value});
  }
 
  handleSubmit(event) {
    alert('Your favorite flavor is: ' + this.state.value);
    event.preventDefault();
  }
 
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          選擇您最喜歡的網站
          <select value={this.state.value} onChange={this.handleChange}>
            <option value="gg">Google</option>
            <option value="rn">Runoob</option>
            <option value="tb">Taobao</option>
            <option value="fb">Facebook</option>
          </select>
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}
 
ReactDOM.render(
  <FlavorForm />,
  document.getElementById('example')
);

 

多個表單

當你有處理多個 input 元素時,你可以通過給每個元素添加一個 name 屬性,來讓處理函數根據 event.target.name 的值來選擇做什麼。

React 實例


class Reservation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };
 
    this.handleInputChange = this.handleInputChange.bind(this);
  }
 
  handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;
 
    this.setState({
      [name]: value
    });
  }
 
  render() {
    return (
      <form>
        <label>
          是否離開:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          訪客數:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}

 


React 事件

以下實例演示通過 onClick 事件來修改數據:

React 實例


class HelloMessage extends React.Component {
  constructor(props) {
      super(props);
      this.state = {value: 'Hello Runoob!'};
      this.handleChange = this.handleChange.bind(this);
  }
  
  handleChange(event) {
    this.setState({value: '菜鳥教程'})
  }
  render() {
    var value = this.state.value;
    return <div>
            <button onClick={this.handleChange}>點我</button>
            <h4>{value}</h4>
           </div>;
  }
}
ReactDOM.render(
  <HelloMessage />,
  document.getElementById('example')
);

 

當你需要從子組件中更新父組件的 state 時,你需要在父組件通過創建事件句柄 (handleChange) ,並作爲 prop (updateStateProp) 傳遞到你的子組件上。實例如下:

React 實例

 


class Content extends React.Component {
  render() {
    return  <div>
              <button onClick = {this.props.updateStateProp}>點我</button>
              <h4>{this.props.myDataProp}</h4>
           </div>
  }
}
class HelloMessage extends React.Component {
  constructor(props) {
      super(props);
      this.state = {value: 'Hello Runoob!'};
      this.handleChange = this.handleChange.bind(this);
  }
  handleChange(event) {
    this.setState({value: '菜鳥教程'})
  }
  render() {
    var value = this.state.value;
    return <div>
            <Content myDataProp = {value} 
              updateStateProp = {this.handleChange}></Content>
           </div>;
  }
}
ReactDOM.render(
  <HelloMessage />,
  document.getElementById('example')
);

 

十四、React Refs

React 支持一種非常特殊的屬性 Ref ,你可以用來綁定到 render() 輸出的任何組件上。

這個特殊的屬性允許你引用 render() 返回的相應的支撐實例( backing instance )。這樣就可以確保在任何時間總是拿到正確的實例。

使用方法

綁定一個 ref 屬性到 render 的返回值上:

<input ref="myInput" />

在其它代碼中,通過 this.refs 獲取支撐實例:

var input = this.refs.myInput;
var inputValue = input.value;
var inputRect = input.getBoundingClientRect();

完整實例

你可以通過使用 this 來獲取當前 React 組件,或使用 ref 來獲取組件的引用,實例如下:

React 實例


class MyComponent extends React.Component {
  handleClick() {
    // 使用原生的 DOM API 獲取焦點
    this.refs.myInput.focus();
  }
  render() {
    //  當組件插入到 DOM 後,ref 屬性添加一個組件的引用於到 this.refs
    return (
      <div>
        <input type="text" ref="myInput" />
        <input
          type="button"
          value="點我輸入框獲取焦點"
          onClick={this.handleClick.bind(this)}
        />
      </div>
    );
  }
}
 
ReactDOM.render(
  <MyComponent />,
  document.getElementById('example')
);

 

實例中,我們獲取了輸入框的支撐實例的引用,子點擊按鈕後輸入框獲取焦點。

我們也可以使用 getDOMNode()方法獲取DOM元素

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章