從零開始的 React 教學 Part 3 - Class Components
Last updated on Oct 2, 2022 in ReactJS 前端框架 by Amo Chen ‐ 3 min read
從零開始的 React 教學 Part 2 中,我們學會利用 Webpack 編譯含有 JSX 的 React 專案,讓我們能夠利用 JSX 語法提高開發 React 的效率。
本篇會將 從零開始的 React 教學 Part 1 中的範例改成 JSX 語法呈現,並且模組化部分組件(components) ,從而達到較高的可維護性。
最後再加上新的外掛 babel/plugin-proposal-class-properties ,讓我們能夠使用 Arrow function 更加簡化 React 程式碼。
本文環境
- Chrome 81
- React 16.13.1
Hello React 進化版
結合前 2 篇所學,我們就能夠將 src/app.jsx
(詳見 從零開始的 React 教學 Part 2 部分程式改用 JSX 語法:
import React from 'react';
import ReactDOM from 'react-dom';
class HelloWorld extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
}
this.addCount = this.addCount.bind(this)
}
addCount() {
let count = this.state.count + 1
this.setState({count: count})
}
render() {
return (
<h1 onClick={this.addCount}>Hello World, {this.props.name} {this.state.count}
</h1>
);
}
}
const domContainer = document.querySelector('#app');
ReactDOM.render(
<HelloWorld name='React' />,
domContainer
);
可以看到第 20 行與第 29 行改成以 JSX 語法實作,程式整體看起來也簡潔許多。
p.s. onClick
是 React 規定的寫法,與 Javascript 的 onclick
不一樣,如果想知道 React 支援什麼 event 可以查閱官方文件 Supported Events 。
模組化
目前為止我們都將所有的程式放在 src/app.jsx
中,對於小型專案而言,並沒有太大影響,然而隨著專案越來越大,模組化就會變成一個至關重要的課題。
得益於 Webpack 與 Babel ,我們能夠輕鬆地使用 ES6 的模組功能 達到模組化 React 專案的目標。
目前有 2 派模組化 Javascript 的做法:
ES6 的模組功能並非屬於上述任何一種,儘管它看起來十分像 CommonJS ,不過它其實是參考了前述 2 者作法而發展出來的。
本篇就不多對 ES6 的模組多做贅述,詳細可以參閱 JavaScript modules - JavaScript | MDN 學習模組的基礎。
接著,試著新增一個模組化的組件,用來為文字加上底線的效果,我們新增 1 個檔案 textDeco.jsx
:
import React from 'react';
class BottomLine extends React.Component {
render() {
return (
<div style={{borderStyle: "solid", borderWidth: "0 0 1px 0"}}>
{this.props.children}
</div>
)
}
}
export default BottomLine;
上述範例的重點在最後 export default BottomLine
的部分,代表我們允許其他人使用 BottomLine
這個組件。
另外值得注意的是 React inline CSS 的寫法,規定我們必須傳 map 型態的物件給 style
才行,這也是為何我們用 style=\{\{borderStyle: "solid", borderWidth: "0 0 1px 0"\}\}
設定 CSS。
p.s. React 的 inline CSS 屬性不需要有 -
,所以 border-style 可以改成 borderStyle, border-width 則改成 borderWidth
如果我們將 style 改成 style="border-style: solid; border-width: 0 0 1px 0;"
就會在瀏覽器的開發者工具出現以下錯誤,這時候只要將 style 改為前述寫法即可:
Uncaught Error: The `style` prop expects a mapping from style properties to values, not a string. For example, style={{marginRight: spacing + 'em'}} when using JSX.
再來我們在 app.jsx
試著使用該組件:
import React from 'react';
import ReactDOM from 'react-dom';
import BottomLine from './textDeco';
class HelloWorld extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
}
this.addCount = this.addCount.bind(this)
}
addCount() {
let count = this.state.count + 1
this.setState({count: count})
}
render() {
return (
<BottomLine>
<h1 onClick={this.addCount}>Hello World, {this.props.name} {this.state.count}
</h1>
</BottomLine>
);
}
}
const domContainer = document.querySelector('#app');
ReactDOM.render(
<HelloWorld name='React' />,
domContainer
);
上述範例第 4 行代表從 textDeco.jsx
匯入 BottomLine
組件,並且在第 22 及第 26 行使用它。
成功的話將會看到 Hello World, 0 多個底線。
以上就是用 Webpack 加上 ES6 進行模組化的過程。
Arrow function (箭頭函式)
本篇最後談談 @babel/plugin-proposal-class-properties · Babel 這個外掛。
目前為止,我們都會在組件的 constructor
方法內為綁定(bind)組件內的方法,例如前述範例 this.addCount = this.addCount.bind(this)
的部分。
但是很多時候我們都會忘記要綁定,導致類似以下的錯誤出現:
app.jsx:54 Uncaught TypeError: Cannot read property 'state' of undefined
這種錯誤總是讓人覺得煩躁,這時候只要安裝 @babel/plugin-proposal-class-properties · Babel 就能夠使用 Arrow function,利用 Arrow function 的特性,從此再也不需要每次都為 React 組件的方法進行綁定。
安裝方法:
$ npm install --save-dev @babel/plugin-proposal-class-properties
安裝完成之後,必須為 Webpack 的設定檔 webpack.config.js
加上新的設定:
const path = require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: {
app: "./src/app.jsx",
},
output: {
path: path.resolve(__dirname, "dist/"),
filename: "[name].js",
publicPath: "/"
},
resolve: {
extensions: [".jsx", ".js"]
},
module: {
rules: [
{
test: /.jsx$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: [
"@babel/preset-react",
"@babel/preset-env"
],
plugins: ['@babel/plugin-proposal-class-properties'],
}
}
},
{
test: /.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: { presets: ["@babel/preset-env"] }
}
},
]
},
plugins: [
new HtmlWebpackPlugin({
title: 'My React App',
filename: 'index.html',
template: "./src/index.html",
})
],
devServer: {
static: {
directory: path.join(__dirname, "dist"),
},
compress: false,
port: 9000
},
}
新增的設定為第 28 行 plugins: ['@babel/plugin-proposal-class-properties'],
。
最後讓我們將 app.jsx
的組件方法改為 Arrow function 並且拿掉綁定的部分吧:
import React from 'react';
import ReactDOM from 'react-dom';
import BottomLine from './textDeco';
class HelloWorld extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
}
}
addCount = () => {
let count = this.state.count + 1
this.setState({count: count})
}
render() {
return (
<BottomLine>
<h1 onClick={this.addCount}>Hello World, {this.props.name} {this.state.count}
</h1>
</BottomLine>
);
}
}
const domContainer = document.querySelector('#app');
ReactDOM.render(
<HelloWorld name='React' />,
domContainer
);
上述範例中:
addCount = () => {
let count = this.state.count + 1
this.setState({count: count})
}
就是一個不需任何參數的 Arrow function ,另外也可以看到 constructor
不需要綁定也能正常運作。
偉哉 Webpack & ES6 。
以上, Happy Coding!
References
https://reactjs.org/