React---基礎1

muzidigbig發表於2020-11-20

react安裝

通過 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

一、什麼是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例子:

React 元素渲染

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

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

與瀏覽器的 DOM 元素不同,React 當中的元素事實上是普通的物件,React DOM 可以確保 瀏覽器 DOM 的資料內容與 React 元素保持一致。 

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

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

ReactDom.render()接受兩個引數,第一個是要被插入的內容,第二個是插入到DOM或者說index.html的位置

一個與Html對比的簡單元件:

class ShoppingList extends React.Componnet {
    render() {
        return (
            <div className="shopping-list">
                <h1>Shoping List for {this.props.name}</h1>
                <ul>
                    <li>Instagram</li>
                    <li>WhatApp</li>
                    <li>Oculus</li>
                </ul>
            </div>
        )
    }
}

// Example usage:  <ShoppingList name="muzidigbig" />

在這裡,ShoppingList是一個 React元件類,或 React元件型別。元件接受引數,稱為屬性 props, 並通過 render方法返回一個現實的檢視層次結構。

render 方法返回您要渲染的內容描述,然後React接受該描述並將其渲染到螢幕上,特別是,render 返回一個React 元素,這是一個渲染內容的輕量級的描述。大多數
React 開發人員使用 JSX 語法,也是上述案例寫到的語法。

JSX 語法的轉換規則為: <div /> 語法在構建是被轉換為 React.createElement('div')。因此,上面的例子等價於:

return React.createElement('div', {className: 'shopping-list'},
    React.createElement('h1', /* h1 children ... */),
    React.createElement('ul', /* ul children ... */)
);

既然 JSX 在 React 開發者中這麼流行,那 JSX 又是什麼呢?
 

二、JSX 語法

JSX :看起來像HTML的 Javascript 一種擴充語法,能夠讓你的 Javascript 中和正常描述 HTML一樣編寫 HTML。

元素是構成 React 應用的最小單位,JSX 就是用來宣告 React 當中的元素。 

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

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

  你可以用 花括號 將任意 Javascript 表示式嵌入到 JSX 中。例如:表示式 1 + 2, 變數 user.firstName, 和函式 formatName(User) 等都可以嵌入使用

function formatName(user) {
    return user.firstName + ' ' + user.lastName;
}

const user = {
    firstName: 'harper',
    lastName: 'Perez'
}

const element = {
    <h1> Hello, {formatName(user)}! </h1>
}

ReactDOM.render (
    element,
    document.getElementById('root')
)

請注意,為了方便閱讀開發者們常將 JSX分割成多行包裹起來,因為這可以避免分號自動插入的陷阱,如

{ 1
2 } 3
// is transformed to
{ 1
;2 ;} 3;

用 JSX 指定屬性值

  你可以用花括號嵌入一個 JavaScript 表示式作為屬性值

// 用引號形式
const element = <div tableIndex="0"></div>;
// 用表示式,並且表示式用花括號包裹
const element = <img src={user.avatarUrl}></img>;

用 JSX 指定子元素

  如果是空標籤,可以直接用 /> 閉合

const element = <img src={user.avatarUrl} />

   如果包含子標籤(外面包裹一個<div>)

<div>
    <h1>Hello!</h1>
    <h2>Good to see you here.</h2>
</div>

比起 HTMLJSX 更接近於Javascript,所以React DOM規範使用駝峰(camelCase)屬性命名約定,而不是HTML屬性名稱,當然,html的部分屬性名稱也作為保留字,不可使用,例如 class
因此,class 在 JSX 中 變為 className, tableindex 變為 tableIndex

用 JSX 防止注入攻擊

  在JSX 中嵌入使用者輸入是安全的:

const title = response.potentiallyMaliciousInput;
// 這樣是安全的
const element  = <h1>{title}</h1>

  預設情況下, 在渲染之前, React DOM 會格式化(escapes) JSX中的所有值. 從而保證使用者無法注入任何應用之外的程式碼. 在被渲染之前,所有的資料都被轉義成為了字串處理。 以避免 XSS(跨站指令碼) 攻擊。

元素渲染到DOM

  正常情況下,你的 index.html 檔案下會有這麼一個div

<div id='root'></div>

  這個root DOM 節點掛在所有React DOM的位置。正常情況下,對於一個React單頁面應用構建,只需要一個單獨的根DOM節點即可。但如果要把React整合到現有的APP中,則可能會使用到多個DOM節點。

  React利用render方法將React元素渲染到DOM上,一旦元素被渲染到頁面了之後,就不能在修改器子元素或任何元素的屬性,就像電影裡的一幀,在某以特定的時間點的UI效果,那元素的更新呢?沒錯,就是重新 render

function tick() {
    cosnt element = {
        <div>
            <h1>Hello, world</h1>
            <h2>It is {new Date().toLocaleTimeString()}.</h2>
        </div>
    };
    ReactDom.render (
        element,
        document.getElementById('root')
    )
}

setInterval(tick, 1000);

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

實際上,大多數 React 應用只會呼叫一次ReactDom.render(),而實現元件更新的辦法就是將程式碼封裝在有狀態的元件中。

React 只更新必須更新的部分

  這正是 React 的強大之處。React DOM 會將元素及其子元素與之前版本逐一對比,並只對有必要更新的 DOM 進行更新, 以達到 DOM 所需的狀態

  開發過程中,更應該每個時間點UI的表現, 而不是關注隨著時間不斷更新UI的狀態, 可以減少很多奇怪的 bug

三、元件和屬性

  元件 components 和屬性 props,其中,屬性是單詞 property 的程式碼簡寫。

定義元件的兩種辦法

  定義元件有兩種方式

  1. 函式式元件定義
  2. 類元件定義
      最簡單的定義元件的方法就是寫一個 Javascript 函式
function Welcome(props)  {
    return <h1>Hello, props.name</h1>
}

  這就是一個有效的元件,它接首了一個 props 引數,並返回了一個React元素,這是一個函式式元件,表面上看,他就是一個 Javascript函式。

  類元件的定義則依賴ES6 的 class 來定義,下面這種定義方法和上方是等效的;

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

渲染一個元件

// DOM標籤作為元件
const element = <div />;
// React 元素作為元件
const element = <Welcome name="muzidigbig" />;

  當React 遇到一個代表使用者定義元件的元素時,它將 JSX 屬性以一個單獨物件即
props物件 的形式傳遞給相應的元件
,例如

function Welcome(props) {
    return <h1>Hello, {props.mname] </h1>;
}
const element = <Wlecome name="muzidigbig" />;
ReactDOM.render(
    element,
    document.getElementById('root')
)

理解

  1. 呼叫 ReactDOM.render() 方法並向其傳入了<Welcome name="muzidigbig" />元素
  2. Raect 呼叫 Welcome 元件,並向其傳入了 {name: 'muzidigbig'} 作為 props物件
  3. Welcome 元件返回 <h1>Hello, muzidigbig</h1>
  4. React DOM 迅速更新 DOM,使其顯示為 <h1>Hello, muzidigbig</h1>

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

構成元件

  既然元件是單獨的一個React元素,那他能單獨工作,因此我們能在一個React 元素中多次引用到相同的元件, 舉個例子:

function Welcome(props) {
    return <h1>Hello, {props.name}</h1>
}
function App() {
    return (
        <Welcome name="Sara" />
        <Welcome name="Lucy" />
        <Welcome name="Edite" />
    )
}

ReactDOM.render(
    <App />,
    document.getElementBuId('root')
)

  通常情況下, React apps 都有一個單獨的頂層的 App 元件。如果是在已有的應用中整合React,也需要由下至上的從小的元件開始逐步整合到檢視頂層的元件中。

元件必須返回一個單獨的根元素,這就是為什麼我們要新增一個 <div>來包裹所有的<Welcome /> 元素的原因

提取元件

  對於一個React 元素,如果其中含有可複用或可能會重複使用的內容,不要害怕把它單拿出來多個更小的元件。

  提取元件可能看起來是一個繁瑣的工作,但是在大型的 App 中可以回報給我們的是大量的可複用元件。一個好的經驗準則是如果你 UI 的一部分需要用多次 (ButtonPanelAvatar),或者本身足夠複雜(AppFeedStoryComment),最好的做法是使其成為可複用元件。

Props 是隻讀的

  無論你用函式或類的方法來宣告元件,

  雖然 React 很靈活,但是它有一條嚴格的規則:**所有 React 元件都必須是純函式,並禁止修改其自身 props **。所謂的純函式就是:傳入函式引數不會在函式執行過程中發生改變,比如自增操作 a++

  如果props是隻讀的,那傳遞給子元素(子元件)的引數豈不是不能修改了?那子元素如何與父元素做互動呢?React還給我們提供了狀態屬性 state供我們在子元件內部修改值

四、狀態和生命週期

  狀態state, 生命週期 liftcircle.
  之前說過,一旦元素被渲染了之後就不可改變了,但我們可以通過重新渲染的方法使頁面得以重新整理,同樣我們提到過最常用的方法是編寫一個可複用的具有狀態的元件,這裡的狀態,就是我們將要說的 state

  我們對上述提過的計時器tick 中的計時功能封裝成一個函式式元件如下:

function Clock(props) {
    return (
        <div>
            <h1>Hello, world!</h1>
            <h2>It is {props.date.toLocaleTimeString()}.</h2>
        </div>
    )
}

  在這個例子中,我們將計時功能程式碼封裝成了一個獨立的可複用的元件,並通過屬性date的方式將引數傳入,但還不能到達我們想要的結果,那就是不能再元件內部修改引數值,元件中顯示的資料依舊受控於父元件中date屬性傳遞過來的值,那如果我們把這個date屬性也新增到Clock內部呢?來看下

ReactDOM.render(
    <Clock />,
    document.getElementById('root')
)

  這時父元件中只保留了對計時元件Clock的一個單純的引用。剩下的事情全部依託以元件Clock自己去實現。要怎麼實現這個需求?這裡React提出了另一個資料物件,即state它用來儲存元件內部的資料,與props類似,不同的是state是元件私有的,並且由元件本身完全控制。它能實現資料在元件內部的修改和更新。怎麼使用這個state?繼續往下講之前我們先擴充一個知識

  我們知道元件有兩種定義方式,即函式式元件和類元件,雖然函式式元件更加簡潔更加接近原生 javascript,但類元件卻擁有一些額外的屬性,這個類元件專有特性,就是狀態生命週期鉤子,到這裡也能清楚知道狀態的關鍵作用,然而函式式元件沒有這兩個特性,因此,在需要使用到狀態state情況下,我們需要將函式式元件轉換成類元件

函式式元件轉化成類元件

  嘗試把一個函式式元件轉化成類元件,官網給出了以下步驟,以Clock元件為例

  1. 建立一個繼承自 React.Component 類的 ES6 class 同名類
  2. 新增一個名為 render() 的空方法
  3. 把原函式中的所有內容移至 render()
  4. render() 方法中使用 this.props 替代 props
  5. 刪除保留的空函式宣告
class Clock extents React.Component {
    render() {
        return (
            <div>
                <h1>Hello, world</h1>
                <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
            </div>
        )
    }
}

  到此,Clock 元件已經成功被我們修改成了一個類元件,我們便可以在其中新增本地狀態state生命週期鉤子

class Clock extends React.Component {
    // 用類建構函式constructor初始化 this.state
    constructor(props) {
        // 使用super()將props傳遞給基礎建構函式
        super(props);
        // 設定初始state
        this.state = {date: new Date()};
    }

    render() {
        return (
            <div>
                <h1>Hello, world</h1>
                <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
            </div>
        )
    }
}

  這樣,我們的類元件Clock 就擁有了自己的屬性 this.state.date,也就不需要引用元件向其傳遞值了,因此,我們可以把元件引用中的date屬性刪掉,最終,我們將其渲染到DOM上,只使用元件引用,其他都交給元件Clock自己去實現

ReactDOM.render(
    <Clock />,
    document.getElementById('root')
)

  到這裡就結束了?細心的你會發現,元件Clock只是實現了當前時間的顯示,而我們要改裝的功能是一個計時器,計時功能去哪裡了?沒實現啊?我們需要在元件Clock中找到一個合適的時機去實現這個功能,為此,React團隊引入了 宣告週期方法,也叫生命週期鉤子

在類元件中新增生命週期方法

  在一個具有許多元件的應用程式中,在元件被銷燬時釋放所佔用的資源是非常重要的。就像瀏覽器的垃圾回收機制,近期內不需要再用的資源,應該及時清除。

  當 Clock 第一次渲染到DOM時,我們要設定一個定時器 。 這在 React 中稱為 “掛載(mounting)” 。它有一個生命鉤子componentDidMount()

Clock 產生的 DOM 被銷燬時,我們也想清除該計時器。 這在 React 中稱為 “解除安裝(unmounting)” 。它的生命鉤子是componentWillUnmount()

  我們的計時器是在頁面載入之後,頁面生成初始化狀態,然後由計時器去觸發狀態的重新整理,因此,在掛載完成是去設定計時器是個非常不錯的選擇

componentDidMount() {
    this.timerID = setInterval(
        () => this.tick(), 1000
    )
}

  掛載時我們宣告瞭一個tick()方法,接下來我們就要實現這個方法,是用來觸發UI更新。嗯哼?UI更新?我們的頁面狀態state不是已經更新了嗎?為啥還要UI更新?

  這裡有一個非常重要的方法:setState()。我們先把程式碼補充完整再說明

componentDidMount() {
    // ...
}

tick() {
    this.setState({
        date: new Date()
    })
}

componentWillUnmount() {
    // ...
}

setState()是React觸發頁面更新的第二個辦法,第一個辦法開篇即說過,即render()方法。setState作用就是通知React檢查帶狀態的元件中是否含有差值。此時react會生成一個虛擬DOM與之前的版本進行對比,只有有必要更新時才會更新。關於 state 與 setState過程 在我的另一篇文章中有詳細說明,有興趣的可以翻過去看看。

  為什麼不把tick()方法寫到componentDidMount()中?因為tick()只是一個普通方法,他不需要在生命週期中觸發,也不用自動觸發。只要誰呼叫了觸發即可。因此不需要也不能放在生命週期鉤子函式中。

  現在這個時鐘每秒都會走了。整理一下,我們整個計時器程式碼如下:

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>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

整個流程的執行過程是這樣的:

  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 得知了元件 state(狀態)的變化, 隨即再次呼叫 render() 方法,獲取了當前應該顯示的內容。 這次,render() 方法中的 this.state.date 的值已經發生了改變, 從而,其輸出的內容也隨之改變。React 於是據此對 DOM 進行更新。

  5. 如果通過其他操作將 Clock 元件從 DOM 中移除了, React 會呼叫 componentWillUnmount() 生命週期鉤子, 所以計時器也會被停止。

正確的使用State(狀態)

  對於setState() 有三件事情是我們應該要知道的

(1)不要直接修改state
  真正觸發React對比不同版本的虛擬DOM是setState() 方法,直接修改state頁面不會重新整理,這一點與原生javascript區別較大,需要理解。

// 這麼做不會觸發React更新頁面
this.state.comment = 'hello';
// 使用 setState() 代替
this.setState({ comment: 'hello' });

【注意】在元件中,唯一可以初始化分配this.state的地方就是建構函式constructor(){}

(2)state(狀態)更新可能是非同步的
  React為了優化效能,有可能會將多個setState() 呼叫合併為一次更新。這就導致 this.propsthis.state 可能是非同步更新的,你不能依賴他們的值計算下一個state(狀態)

// counter 計數更新會失敗
this.setState({
    counter: this.state.counter this.props.increment
})

如果我們有這種需求,可以使用以下setState()辦法:

// ES6 箭頭函式法
this.setState((prevState, props) => ({
    counter: prevState.counter + props.increment
}));
// 常規函式法
this.setState(function(prevState, props) {
    return {
        counter: prevState.counter + props.increment
    };
})

(3)state(狀態)更新會被合併
當你呼叫setState(), React將合併你提供的物件到當前狀態中。例如,你的狀態可能包含幾個獨立的變數,然後你用幾個獨立的setState方法去呼叫更新,如下

constructor(props) {
    super(props);
    this.state = {
        posts: [],
        comments: []
    };
}

componentDidMount() {
    fetchPosts().then(response => {
        this.setState({
            posts: response.posts
        });
    });

    fetchComments().then(response => {
        this.setState({
            comments: response.comments
        });
    });
  }

  合併是淺合併,所以,this.setState({comments})在合併過程中不會改變this.state.posts的值,但是會完全替換this.state.comments 的值。

 

資料向下流動

  無論是作為父元件還是子元件,它都無法或者一個元件是否有狀體,同時也不需要關心另一個元件是定義為函式元件還是類元件。這就是為什麼state經常被稱為 本地狀態封裝狀態 的原因, 他不能被擁有並設定它的元件以外的任何元件訪問。那如果需要訪問怎麼處理?
(1)作為其子元件的props(屬性)

// 在元件中使用
<h2>It is {this.state.date.toLocaleTimeString()}</h2>
// 傳遞給子元件作為props
<FormattedDate date={this.state.date} />

  雖然FormattedDate元件通過props接收了date的值,但它仍然不能獲知該值是來自於Clockstate, 還是 Clockprops, 或者一個手動建立的變數.

  這種資料關係,一般稱為"從上到下"或"單向"的資料流。任何state(狀態)始終由某個特定元件所有,並且從該state匯出的任何資料 或 UI 只能影響樹"下方"的元件

  如果把元件樹想像為 props(屬性) 的瀑布,所有元件的 state(狀態) 就如同一個額外的水源匯入主流,且只能隨著主流的方向向下流動。

各元件完全獨立

  借用上文的Clock元件,我們建立一個App元件,並在其中渲染三個Clock:

function App() {
    return (
        // 之前說過元件只能返回一個根節點,所以用<div>包起來
        <div>
            <Clock />
            <Clock />
            <Clock />
        </div>
    );
}
ReactDOM.render(
    <App />,
    document.getElementById('root')
);

  每個Clock都設立它自己的計時器並獨立更新,如果App中有一個資料變數,也能被三個Clock相互獨立修改。

  至於何時使用有狀態元件,何時使用無狀態元件,被認為是元件的一個實現細節,取決於你當時的需求,你可以在有狀態元件中使用無狀態元件,也可以在無狀態元件中使用有狀態元件

五、事件處理

  通過 React 元素處理事件跟在 DOM 元素上處理事件非常相似。但是有一些語法上的區別:

  1. React 事件使用駝峰命名,而不是全部小寫
  2. 通過 JSX , 傳遞一個函式作為事件處理程式,而不是一個字串
// html usage
<button onclick="todo()">click me</button>
// React usage
<button onClick={todo}>click me></button>

   3.在React中不能通過返回false來阻止預設行為。必須明確的呼叫preventDefault

// html usage
<a href="#" onclick="console.log('clicked'); return false">
    Click me
</a>
// React usage
class ActionLink extends React.Component {
    function handleClick(e) {
        e.preventDefault();
        console.log('clicked.');
    }
    return (
        <a href="#" onClick={handleClick}>
            Click me
        </a>
    )
}

  在這裡,React團隊幫Coder們實現了e事件的跨瀏覽器相容問題。當使用React時,我們也不需要呼叫addEventListener在DOM 元素被建立後新增事件監聽器。相反,只要當元素被初始渲染的時候提供一個監聽器就可以了。

  當使用ES6類定義一個元件時,通常的一個事件處理程式就是類上的一個方法,看個例子,Toggle 元件渲染一個按鈕,讓使用者在 “ON” 和 “OFF” 狀態之間切換:

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('root')
);

繫結類方法

  在JSX回撥中你必須注意 this 的指向。 在 JavaScript 中,類方法預設沒有 繫結 的。如果你忘記繫結 this.handleClick 並將其傳遞給onClick,那麼在直接呼叫該函式時,this 會是 undefined

 這不是 React 特有的行為;這是 JavaScript 中的函式如何工作的一部分,可以使用屬性初始值設定來正確地 繫結(bind) 回撥,但這是實驗性做法,不建議使用,以後有可能會廢棄,如果你沒有使用屬性初始化語法
(1)可以在回撥中使用一個 arrow functions

class LoginButton extends React.Component {
    handleClick() {
        console.log('this is: ', this)
    }

    render() {
        // 這個語法確保 `this` 被繫結在 handleClick 中
        return (
            <button onClick={(e) => this.handleClick(e)}>
                Click me
            </button>
        );
    }
}

(2)使用Function.prototype.bind 方法,相對簡潔方便

<button onClick={this.handleClick.bind(this)}>
    Click me
</button>

傳遞引數給事件處理程式

  在迴圈內部,通常需要將一個額外的引數傳遞給事件處理程式,常用的有一下兩種方案;

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

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

六、條件渲染

在 React 中,你可以建立不同的元件封裝你所需要的行為。然後,只渲染它們之中的一些,取決於你的應用的狀態。

整個元件的條件渲染

  React 中的條件渲染就可在JS中的條件語句一樣,使用JS操作符如if或者條件控制符來建立渲染當前的元素,並且讓React更新匹配的UI。比如我們有一個需求,需要判斷使用者是否登入,來顯示不同元件

function UserGreeting(props) {
    return <h1>Welcome back!</h1>
}

function GustGrreeting(props) {
    return <h1>Please sign up.</h1>
}
function Greeting(props) {
    const isLoggedIn = props.isLoggedIn;
    if (isLoggedIn) {
        return <UserGreeting />
    } 
    return <GuestGreeting />
}

ReactDOM.render(
    <Greeting isLoggedIn={false} />,
    document.getElementById('root')
);

使用元素變數條件渲染部分內容

  你可以用變數來儲存元素。這可以幫助您有條件地渲染元件的一部分,而輸出的其餘部分不會更改。下方兩個元件用於顯示登出和登入按鈕

function LoginButton() {
    return(
        <button onClick={props.onClick}>Login</button>
    )
}

function LogoutButton(props) {
    return (
        <button onClick={props.onclick}>Logout</button>
    )
}

  登入登出按鈕已做好,接下來需要實現有切換功能的一個有狀態的元件,為了更系統化學習,我們把前面的Greeting元件一起加進來

class LoginControl extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            isLoginedIn: 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.bind(this)} />
        } else {
            button = <LoginButton onclick={this.handleLoginClick.bind(this)} />
        }

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

reactDOM.render(
    <LoginControl />,
    document.getElementById('root')
)

使用if是很常見的一種做法,當然也有一些更簡短的語。JSX中有幾種內聯條件的方法,

(1)使用邏輯與&&操作符的內聯if用法
  我們可以在 JSX 中嵌入任何表示式,方法是將其包裹在花括號中,同樣適用於JS的邏輯與&&運算子

function Mailbox(props) {
    const unreadMessages = props.unreadMessages;
    return (
        <div>
            <h1>Hello!</h1>
            { unreadMeaasges.length > 0 &&
                <h2> You have {unreadMessages.length} unread messages.
            }
        </div>
    )
}

cosnt message = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
    <Mailbox unreadMessages={messages} />,
    document.getElementById('root')
);

  該案例是可以正常執行的,因為在 JavaScript 中, true && expression 總是會評估為 expression ,而 false && expression 總是執行為 false 。並且我們可以在表示式中嵌入表示式

(2)使用條件操作符的內聯If-Else(三目運算子)
  條件操作符 即三目表示式:condition ? trueExpression : falseExpression

// 條件渲染字串
<div>The user is {isLoggedIn ? 'currently' : 'not'} logged in.</div>
// 條件渲染元件
<div>
    {isLoggedIn ? (
        <LogoutButton onClick={this.handleLogoutClick} />
    ) : (
        <LoginButton onClick={this.handleLoginClick} />
    )}
</div>

總之,遵循一個原則,哪種方式易於閱讀,就選擇哪種寫法。並且,但條件變得越來越複雜時,可能是提取元件的好時機。

阻止元件渲染

  在極少數情況下,您可能希望元件隱藏自身,即使它是由另一個元件渲染的。為此,返回 null 而不是其渲染輸出。注意這裡是不渲染,不是不顯示

  在下面的例子中,根據名為warnprops 值,呈現 <WarningBanner /> 。如果 props 值為 false ,則該元件不渲染:

function WarningBanner(props) {
    if (props.warn) { 
        return null;
    }
    
    return (
        <div className="warning">Warning</div>
    )
}

class Page extends React.Component {
    constructor(props) {
        super(props);
        this.state = { showWarning: true }
    }

    handleToggleClick() {
        this.setState(prevState => ({
            showWarning: !prevState.showWarning
        }));
    }

    render() {
        return (
            <div>
                <Warningbanner warn={this.state.showWarning} />
                <button onClick={this.handleToggleClick.bind(this)}>
                    { this.state.showWarning ?   'Hide' : 'Show'}
                 </button>
            </div>
        )
    }
}

ReactDOM.render(
    <Page />,
    document.getElementById('root')
)

從元件的 render 方法返回 null 不會影響元件生命週期方法的觸發。 例如, componentWillUpdate componentDidUpdate 仍將被呼叫。因此需要元件剛載入時就要判斷執行返回null