➜ Old React website
Chung Cheuk Hang MichaelJava Web Developer
FFmpeg 基本功能網頁安全

用 React 寫 calculator

Continued from previous post:
React JS 網頁開發筆記(一)

Table of contents

1 React 實作

我地已經安裝曬需要既 IDE、IDE plugin 同埋對 React project 既文件目錄有初步既認識,之後我地可以寫一個非常簡單、純 front-end 冇 back-end 既 React project 黎學下 React、React hooks、JSX syntax、ES6+ 既 JavaScript(ECMAScript)新功能。

2 需求

有一次面試,面試官畀一個鐘我寫一個 calculator program 出黎,可以用 Java 或者 web 寫,功能不限。咁我當然揀用 web(HTML+JavaScript)寫啦。

3 做法既考慮因素

我揀左 React,而我既考慮因素如下:
  • 如果用 Java 寫
    • 一係就 command line,用 Scanner 讀取 user input,冇介面既 calculator 就有啲怪
    • 一係就用 Swing、JavaFX 等等既 GUI framework 黎寫,有埋介面
      • 我以前寫過唔少 JavaFX 既程式
      • 但係鑑於 JavaFX 要 customize 得個介面靚係唔會比 HTML+CSS 易寫
  • 如果用 web 寫
    • 介面容易寫,CSS customize layout 又唔難
    • React
      • 有 one-way binding
      • 用 Create-React-App 生成一個 React template 出黎改,好方便
      • 如果有需要,可以裝 npm packages
      • 而家寫 React app 有曬 hot reloading,就算手動 refresh 都快過 Java
  • 做法
    • 最 basic 既 calulator 係冇算式,每次只係將兩個數用一個 operator(加減乘除)運算出結果
    • 好啲既做法係用戶 append 數字同 operators 落一條 equation 度,個 equation 支援好幾個數,中間有好幾個 operators(加減乘除,甚至括號)
      • 時間有限,唔想寫 algorithm(queue、FIFO)
      • 最簡單就係 pass 成條 calculator 算式既 string 畀個 programming language 自己計
      • 舊版 Java 有 Nashorn JavaScript engine,不過 Java 15 移除左,所以如果將來用新版 Java,呢啲 code 可能會用唔到。雖然我用 Java 8,但最好都係唔寫比較好
      • JavaScript 有 eval() function,我地 calculator 限制數字同幾個 operators,所以唔會有其他人所講既 security risk
    • 用邊個 programming language 以及方法,都一樣會有浮點問題(0.1 + 0.2 唔等於 0.3 而係等於 0.30000000000000004),所以最後都係考慮返易唔易寫、UX、功能數量

4 介面設計

因為我果日帶左部 MacBook Pro 去面試,而 macOS 自帶既 Calculator app 係咁既樣:
咁我地個 calculator 可以參考佢個排版。
1 3 2 1+2 3AC ( ) ⌫ 4 7 8 9 ÷ 5 4 5 6 × 6 1 2 3 − 7 0 . +
除左答案,我地會顯示埋條算式。AC 係 all clear,會刪除曬所有輸入左既字; 係 backspace,會刪除最後一個字。
注意:我地呢個 calculator 係唔需要一個 = 按鍵,咁係因為我地會令佢不斷自動計答案出黎,唔需要撳 = 按鍵先識出答案。

5 動手寫

5.1 用 Create-React-App 建立 project

Make sure 你已經安裝左 npm 同 Create-React-App,如果未,睇返上一篇:React JS 網頁開發筆記(一)
首先是但搵個 folder 執行以下 command:
create-react-app calculator
等一陣之後喺個 project folder 度執行 command:
npm start
就會見到瀏覽器自動彈出我地既網頁,網址係 http://localhost:3000

5.2 寫 React code

根據上一篇 React blog post —— React JS 網頁開發筆記(一) - 了解 src 內既文件,我地先刪除多餘既文件同 code,之後我地就可以加我地既 calculator code 啦。
Project structure:
  • src
    • /components
      • /Calculator
        • index.css
        • index.js
      • /CalculatorButton
        • index.css
        • index.js
    • App.css
    • App.js
    • index.css
    • index.js

5.2.1 Calculator

首先喺 src folder 裡面,開一個 folder 叫 components,然後喺裡面再開一個 folder 叫 Calculator,然後喺裡面開一個檔案叫 index.js
先寫一個空白既 React component:
1function Calculator(props) { 2 return <></>; 3} 4 5export default Calculator; 6export { Calculator };
return <></> 係 JSX 既寫法,詳情睇呢度:7.1 JSX 語法、Babel、Webpack
要做到上面設計既排版,我地會用 <table>。將 return 後面改成:
1<table> 2 <thead> 3 <tr> 4 <th colSpan={4}>Output</th> 5 </tr> 6 </thead> 7 <tbody> 8 <tr> 9 <td colSpan={4}>Equation</td> 10 </tr> 11 <tr> 12 <td>AC</td> 13 <td>(</td> 14 <td>)</td> 15 <td></td> 16 </tr> 17 <tr> 18 <td>7</td> 19 <td>8</td> 20 <td>9</td> 21 <td>÷</td> 22 </tr> 23 <!-- 等等 --> 24 </tbody> 25</table>
每一個 <tr> 都代表一層,而每一個 <td> 就代表一個 table cell。照住呢個 format 將上面改成 4x7。
CSS 少不了,喺同一個 folder 下開一個 index.css 檔,類似:
1table { 2 border-collapse: collapse; 3 border: 1px solid gray; 4} 5 6th, 7td { 8 padding: 8px; 9 text-align: center; 10 width: 2em; 11 line-height: 1.75em; 12 border: 1px solid gray; 13}

5.2.2 CalculatorButton

之後喺 src/components 加一個 CalculatorButton folder,喺裡面加一個 index.js 檔:
1function CalculatorButton(props) { 2 const { children, ...rest } = props; 3 4 return ( 5 <td className="calculator-button" {...rest}> 6 {children} 7 </td> 8 ); 9} 10 11export default CalculatorButton; 12export { CalculatorButton };

5.2.3 令 Calculator 使用 CalculatorButton

去返 Calculator/index.js,我地可以喺最頂加呢句:
import { CalculatorButton } from "../CalculatorButton";
喺個 function component 裡面我地定義一個新 function,呢個 function 會令我地之後撳掣既時候,Chrome developer tools 既 console 顯示撳左既按鍵既字。
const handleClick = (e) => { console.log(e); };
然後將所有:
<td>1</td>
改成:
<CalculatorButton onClick={(e) => handleClick("1")}>1</CalculatorButton>
呢度我地 attach 左一個 onClick 既 event handler,當我地撳一個按鍵,就會 call 裡面既 function。

5.2.4 修改算式

Calculator/index.js 頂頭加呢句:
import { useState } from "react";
再喺 function component 裡面加呢句:
const [equation, setEquation] = useState("0");
呢句畀我地定義一個 React 明既 variable 同埋佢對應既 setter,當 setter 被 call,React 會安排去 update equation 既值。
我地再將 <td colSpan={4}>Equation</td> 改成 <td colSpan={4}>{equation}</td>,咁 React 就會 interpolate equation 既值入呢個 <td> 裡面,將 0 顯示出黎。
要做到撳數字或者運算符號可以令 equation 後面加插果個字,就要將 handleClick function 既 method body 加入一句:
setEquation(equation + e);
不過 AC 既處理方式會有唔同,所以我地要針對性咁改。
首先,我地改 AC 按鍵既 event handler。將 onClick 用既 function 換成一個新既:
<CalculatorButton onClick={() => handleClickAC()}>AC</CalculatorButton>
並且喺 function component 裡面加上呢個 function:
const handleClickAC = () => { console.log("AC is clicked!"); setEquation("0"); };
至於 按鍵,我地要刪除最後輸入既字。將 onClick 用既 function 換成一個新既:
<CalculatorButton onClick={() => handleClickBackspace()}></CalculatorButton>
並且喺 function component 裡面加上呢個 function:
const handleClickBackspace = () => { console.log("Backspace is clicked!"); setEquation(equation.substring(0, equation.length - 1)); };
substring 係 string 既一個 function,第一個 argument 係 start index,喺我地呢個情況就係 equation 值既第一個字,第二個 argument 係 end index + 1("123".substring(0, 2)12 而唔係 123,因為 end index 唔似 start index,後者係 0-based),喺我地呢個情況就係 equation 值既長度減 2。
參考 MDN substringString.prototype.substring()

5.2.5 運算答案

喺 function component 裡面加呢句:
const [output, setOutput] = useState("0");
再將 <th colSpan={4}>Output</th> 改成 <th colSpan={4}>{output}</th>
我地會用 React 自帶既 useEffect hook 黎將 output bind 落 equation 既運算結果。
Calculator/index.js 既頂頭 import 改一改:
import { useState, useEffect } from "react";
喺 function component 裡面加呢段:
1useEffect(() => { 2 try { 3 setOutput(eval(equation.replace(//g, "-") 4 .replace(/×/g, "*") 5 .replace(/÷/g, "/"))); 6 } catch (err) { 7 // do nothing 8 } 9}, [equation]);
useEffect 既第二個 argument [equation] 係一個 array,React 會 mon 住裡面既 variable(s),如果 value(s) 改左就會執行第一個 argument 既 arrow function(() => {})既 method body,用 eval() 黎運算我地既算式。equation 裡面除左 + 號,其他運算符號都要改返做 keyboard 果隻寫法。

5.3 為 IE 瀏覽器 polyfill

為左要令個網頁喺 Internet Explorer 11 瀏覽器上面行得到,我地要為佢 polyfill 啲新版 JavaScript 既 syntax。有一個 library 可以做到呢樣野。
首先,我地執行:
npm i react-app-polyfill
然後喺 package.jsonbrowserslistproductiondevelopment 裡面加上 ie 11
1"browserslist": { 2 "production": [ 3 ">0.2%", 4 "not dead", 5 "not op_mini all", 6 "ie 11" 7 ], 8 "development": [ 9 "last 1 chrome version", 10 "last 1 firefox version", 11 "last 1 safari version", 12 "ie 11" 13 ] 14}
然後喺 src/index.js 既最頂頭加上:
import "react-app-polyfill/ie11"; import "react-app-polyfill/stable";

6 成果

改良一下 CSS 就會得到一個好靚仔既 calculator!
再改良一下 JavaScript 就可以解決 0.1 + 0.2 唔等於 0.3 既問題。

7 心得

7.1 JSX 語法、Babel、Webpack

注意 return <></> 係將 JavaScript 既 return keyword 同 HTML 既 tags <></>(React fragment)寫埋一齊。呢一個並唔係正常既 JavaScript,而係 JSX syntax。目前只有 React 咁寫,係 React 既賣點之一,Angular 同 Vue 都冇(至少佢地既 project template 冇預你用)。因為 Create-React-App 幫我地起呢個 project template 既時候已經加左必須既 React dependencies,而呢啲 React dependencies 背後又各自有佢地既 dependencies,包括 Babel 同 Webpack。
Babel 會將 JSX 轉化為正常瀏覽器睇得明既 JavaScript,亦會 polyfill 部分 JavaScript ES6+ 既新 syntax 或者 functions。Polyfill 既意思係做一層處理,將新版 ES6+ 既 JavaScript 寫法換成舊版 JavaScript 既寫法,令大部分瀏覽器(包括好舊既版本,只睇得明舊版 JavaScript)都睇得明,行得到。Babel 會喺我地 compile 或者 build 個 project 既時候運行,最後我地得到既 for production deployment 用既 code 係經過 Babel 同 Webpack 處理之後既產物,亦都係 minified 左。
Minification 即係我地寫完曬所有 code 之後,Webpack 會將我既啲 code 壓縮,做法就係將所有 variable 名同 function 名都有咁短改咁短,變曬 abcde,同埋冇曬啲隔行。因為其實啲 variable 名、function 名只係為左我地 developer 容易理解所以先用英文詞語寫,對於瀏覽器其實長啲短啲就算換曬 abcde 都冇所謂,因為只要 syntax 岩,reference 岩曬,成件事一樣會 work。因為當用戶上一個網站既時候,瀏覽器需要下載 HTML、CSS、JavaScript 等等既資源,下載速度同所需時間係一個問題。如果我地 minify 左我地既 code,傳輸果時就可以慳返啲 bandwidth,理論上用戶都可以快啲見到個頁面。
參考 React 官網:What's a JSX Transform?
Browsers don't understand JSX out of the box, so most React users rely on a compiler like Babel or TypeScript to transform JSX code into regular JavaScript. Many preconfigured toolkits like Create React App or Next.js also include a JSX transform under the hood.

7.2 Can I use

如果我地想知道邊啲功能有可能有瀏覽器支援問題,或者知道到底適用於現時世界上幾多百分率既瀏覽器,我地可以用 caniuse.com。我地可以喺 Can I use 後面輸入例如:
  • blockquote(HTML tag)
  • draggable(HTML attribute)
  • user-select(CSS property)
  • ::before(CSS selector)
  • Array.filter(JavaScript API)
Firefox 瀏覽器既公司 Mozilla 都有一個 MDN 網站可以畀我地睇到 reference,同埋瀏覽器支援情況。