1. 程式人生 > >React基礎學習

React基礎學習

React 安裝

React 可以直接下載使用,下載包中也提供了很多學習的例項。

本教程使用了 React 的版本為 16.4.0,你可以在官網 https://reactjs.org/ 下載最新版。

你也可以直接使用 Staticfile CDN 的 React CDN 庫,地址如下:

<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script> <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>

<!-- 生產環境中不建議使用 -->

<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>

官方提供的 CDN 地址:

<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script> <script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>

<!-- 生產環境中不建議使用 -->

<script src="https://unpkg.com/babel-s[email protected]/babel.min.js"></script>

NPM管理

npm install --save react react-dom babelify babel-preset-react
npm install --save babel-preset-es2015
使用webpack打包配置
全域性安裝webpack
npm install -g webpack
全域性安裝完webpack還需要專案下安裝npm install webpack --save

全域性安裝webpack開發伺服器
npm install -g webpack-dev-server
全域性安裝完webpack-dev-server 後還需要在當前目錄下安裝npm install webpack-dev-server --save

 

 

使用例項

以下例項輸出了 Hello, world!

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>

<div id="example"></div>
<script type="text/babel">
ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('example')
);
</script>

</body>
</html>

例項解析:

例項中我們引入了三個庫: react.min.js 、react-dom.min.js 和 babel.min.js:

  • react.min.js - React 的核心庫

  • react-dom.min.js - 提供與 DOM 相關的功能

  • babel.min.js - Babel 可以將 ES6 程式碼轉為 ES5 程式碼,這樣我們就能在目前不支援 ES6 瀏覽器上執行 React 程式碼。Babel 內嵌了對 JSX 的支援。通過將 Babel 和 babel-sublime 包(package)一同使用可以讓原始碼的語法渲染上升到一個全新的水平。

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('example')
);
以上程式碼將一個 h1 標題,插入 id="example" 節點中。
注意:

如果我們需要使用 JSX,則 <script> 標籤的 type 屬性需要設定為 text/babel。

通過 npm 使用 React

國內使用 npm 速度很慢,你可以使用淘寶定製的 cnpm (gzip 壓縮支援) 命令列工具代替預設的 npm:

$ npm install -g cnpm --registry=https://registry.npm.taobao.org
$ npm config set registry https://registry.npm.taobao.org
這樣就可以使用 cnpm 命令來安裝模組了:
$ cnpm install [name]

使用 create-react-app 快速構建 React 開發環境

create-react-app 是來自於 Facebook,通過該命令我們無需配置就能快速構建 React 開發環境。

create-react-app 自動建立的專案是基於 Webpack + ES6 。

執行以下命令建立專案:

$ cnpm install -g create-react-app
$ create-react-app my-app
$ cd my-app/
$ npm start

manifest.json 指定了開始頁面 index.html,一切的開始都從這裡開始,所以這個是程式碼執行的源頭。

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
render() {
  return (
    <div className="App">
      <div className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <h2>歡迎來到hello world</h2>
      </div>
      <p className="App-intro">
        你可以在 <code>src/App.js</code> 檔案中修改。
      </p>
    </div>
  );
}
}

export default App;

React 元素渲染

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

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

將元素渲染到 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 編寫模板更加簡單快速。

使用 JSX

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

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

我們可以在以上程式碼中巢狀多個 HTML 標籤,需要使用一個 div 元素包裹它,例項中的 p 元素添加了自定義屬性 data-myattribute,新增自定義屬性需要使用 data- 字首。

ReactDOM.render(
  <div>
      <h1>hello world</h1>
      <h2>歡迎學習 React</h2>
      <p data-myattribute = "somevalue">這是一個很不錯的 JavaScript 庫!</p>
  </div>
  ,
  document.getElementById('example')
);

獨立檔案

你的 React JSX 程式碼可以放在一個獨立檔案上,例如我們建立一個 helloworld_react.js 檔案,程式碼如下:

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

然後在 HTML 檔案中引入該 JS 檔案:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Hello React!</title>
  <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
  <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
  <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>
  <div id="example"></div>
  <script type="text/babel" src="./hellow_react.js"></script>
</body>
</html>

JavaScript 表示式

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

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

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

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

樣式

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

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

註釋

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

ReactDOM.render(
  <div>
  <h1>hello world</h1>
  {/*註釋...*/}
    </div>,
  document.getElementById('example')
);

陣列

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

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Hello React!</title>
  <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
  <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
  <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>
  <div id="example"></div>
  <script type="text/babel">
      var arr = [
          <h1>cainiaojiaocheng</h1>,
          <h2>是時候展示真正的技術了</h2>,
      ];
      ReactDOM.render(
          <div>{arr}</div>,
          document.getElementById("example")
      )
  </script>
</body>
</html>

HTML 標籤 vs. React 元件

React 可以渲染 HTML 標籤 (strings) 或 React 元件 (classes)。

要渲染 HTML 標籤,只需在 JSX 裡使用小寫字母的標籤名。

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

要渲染 React 元件,只需建立一個大寫字母開頭的本地變數

var MyComponent = React.createClass({/*...*/});
var myElement = <MyComponent someProperty={true} />;
ReactDOM.render(myElement, document.getElementById('example'));

React 的 JSX 使用大、小寫的約定來區分本地元件的類和 HTML 標籤。

注意:

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

React 元件

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

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Hello React!</title>
  <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
  <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
  <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>
  <div id="example"></div>
  <script type="text/babel">
      function HelloCompent(params) {
          return <h1>hellow amisu</h1>;
      }
      const element = <HelloCompent/>
      ReactDOM.render(
          element,
          document.getElementById("example")
      )
  </script>
</body>
</html>

例項解析:

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 物件,例項如下:

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 的保留字。

複合元件

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

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

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Hello React!</title>
  <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
  <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
  <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>
  <div id="example"></div>
  <script type="text/babel">

      function Name(props) {
          return <h1>網站名稱:{props.name}</h1>;
      }
      function Url(props) {
          return <h1>網站地址:{props.url}</h1>;
      }
      function App() {
          return (
          <div>
              <Name name="hello world" />
              <Url url="http://www.runoob.com" />
          </div>
          );
      }
      ReactDOM.render(
          <App/>,
          document.getElementById("example")
      )
  </script>
</body>
</html>
例項中 App 元件使用了 Name、Url 元件來輸出對應的資訊。

React State(狀態)

React 把元件看成是一個狀態機(State Machines)。通過與使用者的互動,實現不同狀態,然後渲染 UI,讓使用者介面和資料保持一致。

React 裡,只需更新元件的 state,然後根據新的 state 重新渲染使用者介面(不要操作 DOM)。

以下例項建立一個名稱擴充套件為 React.Component 的 ES6 類,在 render() 方法中使用 this.state 來修改當前的時間。

新增一個類建構函式來初始化狀態 this.state,類元件應始終使用 props 呼叫基礎建構函式。

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 中被稱為解除安裝

我們可以在元件類上宣告特殊的方法,當元件掛載或解除安裝時,來執行一些程式碼:

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 的屬性、亦或手工輸入:

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

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 來傳遞資料。

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

預設 Props

你可以通過元件類的 defaultProps 屬性為 props 設定預設值,例項如下:

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 來獲取父元件傳遞過來的資料。

class WebSite extends React.Component {
  constructor() {
      super();
 
      this.state = {
        name: "hello world",
        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 (
    	//ES6方法接收引數用的是this.props.xxx,函式式直接props.xx
      <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')
);

React 事件處理

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

  • React 事件繫結屬性的命名採用駝峰式寫法,而不是小寫。

  • 如果採用 JSX 的語法你需要傳入一個函式作為事件處理函式,而不是一個字串(DOM 元素的寫法)

HTML 通常寫法是:

<button onclick="activateLasers()">
  啟用按鈕
</button>

React 中寫法為:

<button onClick={activateLasers}>
  啟用按鈕
</button>

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

<a href="#" onclick="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中,你可以建立不同的元件來封裝各種需求行為,然後還可以根據應用的狀態變化只渲染其中的一部分。然後還可以根據應用的狀態變化只渲染其中的一部分。

先來看兩個元件:

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

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

我們將建立一個 Greeting 元件,它會根據使用者是否登入來顯示其中之一:

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

元素變數

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

在下面的例子中,我們將要建立一個名為 LoginControl 的有狀態的元件。

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

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>React 例項</title>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>
<div id="example"></div>

<script type="text/babel">
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;

    if (isLoggedIn) {
      button = <LogoutButton onClick={this.handleLogoutClick} />;
    } else {
      button = <LoginButton onClick={this.handleLoginClick} />;
    }

    return (
      <div>
        <Greeting isLoggedIn={isLoggedIn} />
        {button}
      </div>
    );
  }
}

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

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

function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;
  if (isLoggedIn) {
    return <UserGreeting />;
  }
  return <GuestGreeting />;
}

function LoginButton(props) {
  return (
    <button onClick={props.onClick}>
      登陸
    </button>
  );
}

function LogoutButton(props) {
  return (
    <button onClick={props.onClick}>
      退出
    </button>
  );
}

ReactDOM.render(
  <LoginControl />,
  document.getElementById('example')
);
</script>

</body>
</html>

與運算子 &&

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

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 的條件運算子:condition ? true : false。
render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      {isLoggedIn ? (
        <LogoutButton onClick={this.handleLogoutClick} />
      ) : (
        <LoginButton onClick={this.handleLoginClick} />
      )}
    </div>
  );
}

阻止元件渲染

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>React 例項</title>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
<style>
button {
  height: 40px;
  width: 200px;
}
.warning {
  background-color: red;
  text-align: center;
  width: 100%;
  padding: 10px;

  font-size: 14pt;
  color: white;
}
</style>
</head>
<body>
<div id="example"></div>

<script type="text/babel">
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')
);
</script>

</body>
</html>

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

React 列表 & Keys

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

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

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

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

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 可以在 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的正確使用方式

function ListItem(props) {
  // 對啦!這裡不需要指定key:
  return <li>{props.value}</li>;
}
 
function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    // 又對啦!key應該在陣列的上下文中被指定
    <ListItem key={number.toString()}
              value={number} />
 
  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}
 
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('example')
);

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

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

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()

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

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

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

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

React 元件 API

  • 設定狀態:setState

  • 替換狀態:replaceState

  • 設定屬性:setProps

  • 替換屬性:replaceProps

  • 強制更新:forceUpdate

  • 獲取DOM節點:findDOMNode

  • 判斷元件掛載狀態:isMounted

設定狀態:setState:

setState(object nextState[, function callback])

引數說明

  • nextState,將要設定的新狀態,該狀態會和當前的state合併

  • callback,可選引數,回撥函式。該函式會在setState設定成功,且元件重新渲染後呼叫。