(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 繼承類別名稱
。
請打開src資料夾底下的App.js。在標頭引入React class Component
import React, { Component } from 'react';
用extends
去繼承Component
,也就是整個程式碼變成:
import React, { Component } from 'react'; // 引入React定義好的React Component
class App extends Component{ //繼承Component類別
}
export default App;
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。
在ES6的class宣告member data,直接在constructor()
宣告,在前面要加上this
關鍵字,當有多個以此class宣告的物件時,單一物件就會用this
知道這個member data是屬於自己的。
class 物件名稱{
constructor(){
this.宣告名稱=初始值;
}
}
此外,如果你在任何member function寫出了
this.宣告名稱=某值;
如果該函式被呼叫時,物件不存在該名稱的member data,就會直接幫你在物件建立該member data。
在 class component 中使用 props 跟 function component的使用方法有兩個差別:
在使用前必須在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()
就能取得所繼承的類別中的變數結構。
使用時和使用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只有當:
這兩個狀況發生時,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。
請剪下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')
);
在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")
}
在constructor中綁定changeName。
constructor(props) {
super(props);
this.name="舊的名字";
this.changeName=this.changeName.bind(this); //綁定至自己
}
修改綁在按鍵上的內容
render(){
return(
<button onClick={this.changeName}>{this.name} </button>
/* 修改onClick和name */
);
}
做到這邊函式綁定就完成了。但是,你會發現跟上面講props時一樣的狀況(按下按鍵後,console訊息有出現(代表函式有被觸發),名字卻並沒有被改變。)
在下一篇,我們就會用state
來解決這個問題。
目前正在跟著你的文章學習,謝謝你,解說的很詳細。
謝謝你的肯定!如果有什麼不清楚的地方都可以留言跟我說~