iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 11
0
Modern Web

給初入JS框架新手的React.js入門系列 第 11

【React.js入門 - 11】 開始進入class component

  • 分享至 

  • xImage
  •  

(2024/04/06更新) 因應React在18後更新了許多不同的語法,更新後的教學之後將陸續放在 新的blog 中,歡迎讀者到該處閱讀,我依然會回覆這邊的提問


很多時候,我們想要做的元件無法用函式的架構完整呈現,而是希望像class一樣,整理多個函式、資料。所以,接下來我們就要介紹class component的使用方法。

React的class component以ES6的class為架構,以下會有很多部分是在講解和class有關的語法使用,可以配合其他專門說明ES6 class的文章了解詳細的原理。

在開始此篇前,建議有ES6或是c-like語言的物件導向觀念。

宣告

ES6的class宣告方式跟c-like語言很像,也就是

class 物件名稱{
    constructor(){
    
    }
    
    函式A名稱(){
     
    }
    函式B名稱(){
     
    }
}

而React component就是在ES6 class的架構下,繼承定義在 React 的 component 物件類別。繼承的方式為在宣告時加入extends 繼承類別名稱

  1. 請打開src資料夾底下的App.js。在標頭引入React class Component

    import React, { Component } from 'react';
    
  2. extends去繼承Component ,也就是整個程式碼變成:

    import React, { Component } from 'react'; // 引入React定義好的React Component
    class App extends Component{ //繼承Component類別
    
    }
    export default App;
    
  3. React component的特性是,會用render()函式去收集要渲染到畫面上的東西(放在return值),再去改變DOM。
    請在App class中加入此member function:

        render(){
            return(
              <div>
                helloWorld
              </div>
            );
        }
    

    App.js中所有的程式碼:

    import React, { Component } from 'react';
    class App extends Component{ 
        render(){
            return(
              <div>
                helloWorld
              </div>
            );
        }
    }
    export default App;
    

    畫面上就會出現hello world的div。

    在React component渲染DOM前/後其實是有一連串的流程 (a.k.a生命週期),render()是在渲染前最後一個階段(有個例外,但那個例外很少用),我們會在後面來詳細講整個流程。

    請注意render()只是渲染前最後一個階段,元件還沒有真的渲染到DOM上。所以不要在render()中操作有關return元素的DOM。

宣告class的member data

在ES6的class宣告member data,直接在constructor()宣告,在前面要加上this關鍵字,當有多個以此class宣告的物件時,單一物件就會用this知道這個member data是屬於自己的。

class 物件名稱{
    constructor(){
        this.宣告名稱=初始值;
    }
}

此外,如果你在任何member function寫出了

this.宣告名稱=某值;

如果該函式被呼叫時,物件不存在該名稱的member data,就會直接幫你在物件建立該member data。

props的使用

在 class component 中使用 props 跟 function component的使用方法有兩個差別:

  1. 在使用前必須在constructor加上super(props)

    請打開src資料夾底下的App.js
    並修改為:

    import React, { Component } from 'react';
    class App extends Component{
        constructor(props) { // 加入建構子以及props參數
            super(props);
        }
    
    
    export default App;
    

    因為我們這邊的 props 是使用在繼承的 Component 類別中所定義好的變數結構。在js中,透過super()就能取得所繼承的類別中的變數結構

  2. 使用時和使用js的class的member data一樣,需要加上this。

    請修改App class中的render函式:

        render(){
            return(
              <button onClick={this.props.handleClick}>{this.props.name}</button>
            );
        }
    

    同時在先前的index.js中的changeName中,加入console.log("hey"):

    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import App from './App';
    import * as serviceWorker from './serviceWorker';
    
    const changeName=(newName)=>{ 
        name=newName;
        console.log("hey")//加入此行
    }
    
    var name="舊的名字";
    
    ReactDOM.render(
        <div>
            <App name={name} handleClick={changeName}/>
        </div>,
        document.getElementById('root')
    );
    

    畫面上就會的按鍵按一下後,名字就會被改變...嗎?

    你會發現按下按鍵後,console訊息有出現(代表函式有被觸發),名字卻並沒有被改變。為什麼呢?這是因為先前我們提過,React component只有當:

    1. props的值改變時
    2. state的值改變時

    這兩個狀況發生時,ReactDOM才會進入re-render該component的update程序,更新畫面。state是React class component的一個特別的member data(先不論React hook),我們在下一篇就會來講他。而這邊index.js的name並不是一個state,所以實際DOM上的App並沒有被重新渲染,也就是其propsname沒有改變。

函式

在ES6的class中,當使用同class scope的其他member function時,必須要使用 this.函式名稱 = this.函式名稱.bind(this)綁定此物件。這是因為javascript的this在class的 member function中是指向undefined。現在,我們來把原本在index.js的changeName()移至App.js。

  1. 請剪下index.js中的changeName以及var name="舊的名字",並移除App的所有props。

    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import App from './App';
    import * as serviceWorker from './serviceWorker';
    
    ReactDOM.render(
        <div>
            <App/>
        </div>,
        document.getElementById('root')
    );
    
  2. 在App.js的App class中,加入changeName函式name變數

    import React, { Component } from 'react';
    class App extends Component{
    constructor(props) { 
       super(props);
       this.name="舊的名字"; // 加入name
     }
    
    changeName(newName){ // 定義changeName
       this.name=newName;
       console.log("hey")
     }
    
    
  3. 在constructor中綁定changeName。

    constructor(props) { 
        super(props);
        this.name="舊的名字";
        this.changeName=this.changeName.bind(this); //綁定至自己
    }
    
  4. 修改綁在按鍵上的內容

    render(){
        return(
          <button onClick={this.changeName}>{this.name} </button>
          /* 修改onClick和name */
        );
    }
    

做到這邊函式綁定就完成了。但是,你會發現跟上面講props時一樣的狀況(按下按鍵後,console訊息有出現(代表函式有被觸發),名字卻並沒有被改變。)

在下一篇,我們就會用state來解決這個問題。


上一篇
【React.js入門 - 10】 夾在中間的props: children
下一篇
【React.js入門 - 12】 state 與 詳解setState語法
系列文
給初入JS框架新手的React.js入門31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
nagiMemo
iT邦新手 5 級 ‧ 2020-05-30 13:45:10

目前正在跟著你的文章學習,謝謝你,解說的很詳細。

Andy Chang iT邦研究生 3 級 ‧ 2020-05-31 13:09:47 檢舉

謝謝你的肯定!如果有什麼不清楚的地方都可以留言跟我說~

我要留言

立即登入留言