---
# System prepended metadata

title: 1.21 React
tags: [React]

---

# 1.21 React

###### tags: `React`

## 零、目錄&待辦事項

## 壹、ES6複習

### 一、let、const
#### 1. 函式表達式用const來宣告較佳
```javascript=
const foo = function(){...}
```
#### 2. 區域作用域 vs 函式作用域
* 區域作用域：if/else、for迴圈的{}裡面；ES6開始在這裡面就是local
    * for迴圈()中的let變數仍然是在local
    * for迴圈中的let變數會重新綁定
* 函式作用域：function的{}裡面；ES5前在這裡面才是local

```javascript=
for (var i = 0; i < 10; i++){
    setTimeout(()=> console.log('i:', i), 1000)
}
// Output: i:10; 10組
// var i 是全域變數，所以最後會變成10
// 在執行的時候，JS是去記i的記憶體位置，也就是每次都是記得i，而不是那個值，最後顯現時才把它全部印出來

for(let i = 0; i < 10; i++){
    setTimeout(()=> console.log('i:', i), 1000)
}
// Output: i:0, i:1, ..., i:9
// let i 是區域變數，每次都會綁定一次
// 所以最後會是10個不同的i
// 這個相當於下面的寫法

let k;
for(k = 0; k < 10; k++){
    let i = k;
    setTimeout(()=>console.log('i:', i), 1000)
}

```

### 二、箭頭函式
* 箭頭函式複習
```javascript=
const foo1 = function(x){ return x + 1 }
const foo2 = (x) => x + 1
// 省略function、{}、return
```
```javascript=
const foo2 = x => x + 1
// foo2(10); 
// output: 11

const foo3 = x => { x + 1 }
// foo3(10); 
// output: undefined

// 只有單一參數時，可以再省略()
// 只有單一回傳值(一行)時，可省略花括號跟return，才會有自動腦補return
// 如果沒有省略花括號，則沒有腦補return
```
* JSX語法搭配箭頭函式
JSX語法有很多列，此時可以用()作為分行的排版符號，放在()內的會被視為同一行，所以可以省略{}、return
```javascript=
const HelloWorld = (props) => (
    <div>
        <h1>{props.text}</h1>
    </div>
    )
// <div><h1>{props.text}</h1></div> 是一行
```
* 箭頭函式和一般函式的差別
![](https://i.imgur.com/vhKN5ht.png)

* 箭頭函式沒有arguments物件，這裡Eddy沒有解釋什麼，只說了這是個奇怪的東西
```javascript=
const bar = function(x,y){ console.log(arguments)}
bar(1,2);
// 有一串奇怪的東西
bar(1,2,5);
//這樣也跑得出來喔
```

### 三、傳入預設值
* 在沒有給參數或是給的式undefined時，才會使用預設值
* null的話會是null，不會用預設值
* JS的空值是undefined，不是null

### 四、解構賦值

### 五、展開與其餘運算符
* 陣列跟物件都適用展開運算符(ES8)
```javascript=
const newArray = [...arr, b]
// 相當於把b push到原陣列內
```
* 其餘運算符
```javascript=
function(...a){}
// 不特定個數的參數

const[a, ...b] = [1,2,3]
// a=1、b=2,3

```

### 六、類別

### 七、模組系統
* Export & Import
* [可以看這篇的介紹](https://wcc723.github.io/development/2020/03/25/import-export/)
* 模組系統不是每個瀏覽器都可以使用，還是會用babel轉譯，caniuse有解釋各瀏覽器運作

#### 1. Export
* Export 可以將函式、物件甚至是純值匯出，大部分運用上都是匯出物件（函式）為主，畢竟純值作為模組意義並不大。
* 分為Export default 預設導出、Export 具名導出
* Export default 預設導出：一支程式只能有一個預設導出，通常導出函式、類別(不要導出變數、常數)
* Export 具名導出

## 貳、React技術入門

### 一、React是什麼
react是JS的函式庫，用來建立使用者的操作介面

### 二、五大特性
虛擬DOM、JSX語法、元件化、單向資料流、宣告式程式設計

#### 1. 虛擬DOM - React自創的DOM元素與結構語法
* DOM：Doucment Object Model，文件物件模型
    * DOM指的就是HTML的程式介面，提供了樹狀的結構化呈現，每一個節點就是一個DOM節點 。
* 虛擬DOM是react自創管理的dom結構，經與真實DOM差異比較並調和一致(reconciliation)後，再做渲染(render)。
    * 產生頁面就叫做渲染，白話為轉譯和呈現
* 為何要用虛擬DOM?
    * 現在的網頁太複雜，開發者用真實DOM太難寫了。像是FB有各種功能，不好寫。
    * React處理速度其實不會比直接DOM處理更快，只是他協助開發者建立較好維護的迎用程式。

#### 2.JSX語法 - 搭配虛擬DOM的語法，類似於HTML
* JSX：JS語法的擴充，JS=JavaScript，X=XML
* JSX中{}類似於樣板字串，可以用於代入變數、求值運算
* 這裡的JSX不是一般的JSX，是react自創的JSX
* 需要透過babel編譯才能執行
    * babel是JS的編譯器。JS是直譯語言，為何還需要編譯?
    * JS功能越來越多，且支援的裝置越來越多樣化，相容性要求越來越高，因此需要轉譯。
```javascript=
const element = <div>Hello World</div>
// 這其實瀏覽器看不懂，要再經過babel編譯
// 是React.createElement的簡寫，經babel編譯後相當於下面的寫法

var element = React.createElement(
    "div",
    null,
    "Hello World"
)
```

#### 3.元件化-開發採用元件分離與組合的方式
依照功能性拆解成多個小元件，開發多個小元件再組合

#### 4.單向資料流-從父母元件(擁有者)流到子女元件(被擁有者)(從外到內)
利用單向流達到元件化，目前只有單向(其餘框架大多雙向)

#### 5.宣告式程式設計-如何更動與呈現交由React
指令式：明確告知每一步怎麼做
宣告式：告知電腦目標就好，詳細步驟交給他(例如SQL語法)

### 三、react16新核心-React Fiber
上課是以16版為主。
* ???
* 太難啦
* React Fiber(纖呈)：讓工作程序更小，達到並行的異步渲染(Async Rendering)



## 參、React環境設定與安裝

[相關細節還有設定檔可以看這裡](https://github.com/eyesofkids/mfee11-react/tree/main/%E6%95%99%E6%9D%90/0121/reactjs%E9%96%8B%E7%99%BC%E7%92%B0%E5%A2%83%E8%A8%AD%E5%AE%9A/CRA-eslint-prettier)

### Step.1 安裝yarn
```
npm install --global yarn
```
yarn指令：
1. yarn start：啟動伺服器
2. yarn build：專案發佈到線上時用的(大專只用一點點)
3. yarn test：模擬網頁上的使用者操作行為(大專不會用到)
4. yarn eject：跳出設定檔，永久性的動作，無法回復(大專不會用到)。設定檔藏在node_module裡面有個react-scripts，裡面有config
### Step.2 建立React專案
[協助建立react專案的官方文件](https://create-react-app.dev/docs/getting-started/)

```
目標資料夾內cmd npx create-react-app 專案名稱
```
### Step.3 安裝ESlint & Prettier模組以及各自的VScode套件
* 安裝好後還要載入Eddy的設定檔，npm模組跟VScode都有各自的設定檔
* babel在安裝react專案時就會自動安裝
* 都安裝完後，檢查右下角是不是有打勾的ESLint，有的話就成功了。如果是紅色的就按下去啟動。
### Step.4 啟動伺服器 yarn start
### Step.5 由.env更改埠號
在react專案資料夾內建立一個.env檔，裡面寫PORT=3006

## 肆、檔案結構介紹

### 1. index.html(這隻先不會動)：
* 也就是local:3000，入口的html檔案。
* 內容只有一個root元素，也沒有script。所有的DOM元素都會渲染到這個節點。
* 除非要引入一些特別的CSS才會動到這個檔案，通常不會動。
### 2. index.js(這隻先不會動)：
* 對應到index.html。
* ReactDOM.render：有二個參數，一個element跟一個dom，把app這個元件渲染到root，也就是所謂的把虛擬dom轉為真實dom。
* React.StrictMode：React.StrictMode > react會自動檢查開發的元件是不是有問題。可寫可不寫。
### ３. App.js：
* 是個元件
* 元件分為函式型及類別型

```javascript=
// App.js
// 第一行在以前的版本要寫，但後來更新版本會自動引入這行
// 因為有JSX語法，JSX式react.createlement的簡寫，所以要有import
import React from 'react'

function App(){
    return <h1>Hello</h1>
}

expor default App
```

## 伍、函式型元件
* react元件二大重點：屬性、狀態
開發者透過元件的狀態，告訴react如何改變網頁上的內容
* 鉤子是什麼?
    * react的hook(鉤子)其實就是react內建的函式、API，綁定react一些功能
    * useState 是鉤子的一種，會產生二個值分別為得到狀態、設定狀態
    * 經由useState這個鉤子，創造total這個狀態，並給予初始值為0。以及創造setTotal這個設定狀態的方法。
* [元件撰寫原則](https://github.com/eyesofkids/mfee11-react/issues/2)
    * 資料類型、建構函式大駝峰，方法小駝峰
    * js檔名只看得懂$、_這二個符號(但不要用)

### 一、計數器
```javascript=
// 程式碼/0121/1.計數器/App.js
// 這行是因為React用JSX語法，React.createElement的簡寫，所以需要import
// 導入useState這個API
// 以前的函式型元件不能使用狀態，hook後才可以
// 部分導入，所以是{}

import{ useState } from 'react'

function App(){
    // 宣告一個新的state變數，名稱為"total"
    // 這段語法是解構賦值的語法
    // useState會產生陣列，內有二個值，初始值為0，total得到狀態、setTotal設定狀態
    const[total, setTotal] = useState(0)
    return (
    <>
        <h1 onClick={()=>{
        setTotal(total+1)
        }}>
        {total}
        </h1>
    </>
    )
}
export default App

// 這是假的、人造的h1，讓react模仿h1，做一個onclick的屬性，要做setTotal這件事情。
// 這是間接的處理，原本JS的const不能更改
// react要改變狀態就只能用設定狀態的方法
// return只能有一個東西，所以要把h1跟btn放在空的<>內
```
### 二、計數器-按鈕
```javascript=
// 程式碼/0121/2.計數器-用按鈕/App.js
import{ useState } from 'react'

function App(){
    const[total, setTotal] = useState(0)
    return (
    <>
        <h1>{total}</h1>
    // 多了button後會跳eslint error，JSX must be wrapped in an enclosing tag (必須具有一個父元素)
        <button onClick={()=>{
        setTotal(total+1)
        }}>+1</button>
        
        <button onClick={()=>{
        setTotal(total-1)
        }}>-1</button>
    </>
    // h1、btn是三棵樹種在一片土地上。只能return一棵樹。所以要用fragment把h1、btn*2包裹起來變成一棵樹。
    // 回傳的JSX語句只能有一個跟元素。必要時須使用<>...</>包住
        
    )
}
export default App
```

### 三、拆解App、CountFunc元件

```javascript=
// App.js 
// 程式碼是寫在CountFunc，在App內使用
import CountFunc from './components/CountFunc'

function App(){
    return(
        <>
            <CountFunc />
        </>
    )
}


// components/CountFunc.js
import {useState} from 'react'

function CountFunc(){
    const[total, setTotal] = useState(0)
    
    return(
        <>
        <h1>{total}</h1>
        <button onClick = {()=>{
            setTotal(total+1)
        }}>+1</button>
        <button onClick{()=>{
            setTotal(total-1)
        }}>-1</button>
        
        </>
    )
}

export default CountFunc

```

### 四、類別型元件語法
```javascript=
// 類別型元件較麻煩，一定要有constructor跟render，還要使用react內建的setState

import React from 'react'

class CountClass extends React.Component {
  constructor() {
    super()
    this.state = {
      total: 0,
    }
  }
  render() {
    return (
      <>
        {/* 和函式型元件不同，這裡是使用物件的寫法 */}
        <h1
          // JS內是onclick="..."，所以變成onClick={...}
          onClick={() => {
            // setState是來自於react.component
            // 裡面要放的是物件
            this.setState({ total: this.state.total + 1 })
          }}
        >
          {this.state.total}
        </h1>
      </>
    )
  }
}

export default CountClass


```

## 伍、類別(不是這堂課的重點)
[類別的講義在這裡](https://github.com/eyesofkids/mfee11-react/blob/main/%E6%95%99%E6%9D%90/0121/ES6%E7%AF%87-%E9%A1%9E%E5%88%A5class.pdf)
但很多都跳過RRR

### 一、this指的是誰
在類別型物件中，this指的是由new方法所建立出來的物件實體


## 陸、元件的狀態(state)與屬性(props)
[看看這裡還不錯](https://hackmd.io/@Heidi-Liu/note-fe302-component-jsx)
* React的核心思想就是元件化思想，頁面會被切分成一些獨立的、可複用的元件。
* 元件從概念上看就是一個函式，可以接受一個引數作為輸入值，這個引數就是props，所以可以把props理解為從外部傳入元件內部的資料。由於React是單向資料流，所以props基本上也就是從服父級元件向子元件傳遞的資料。
* 一個元件的顯示形態可以由資料狀態和外部引數所決定，外部引數也就是props，而資料狀態就是state。
* state不同於props的一點是，state是可以被改變的。不過，不可以直接通過this.state=的方式來修改，而需要通過this.setState()方法來修改state。
* state是元件內部的私有資料，用於儲存因時間經過或是使用者操作而變動的資料，可變；
* props是外部傳入的資料引數，不可變(只有父母元件能變動)；
* 沒有state的叫做無狀態元件，有state的叫做有狀態元件；
* 多用props，少用state。也就是多寫無狀態元件。
* setState方法是非同步的

## 柒、SelectBox.js
### 一、Q：import {useState} from 'react'、import React from 'react'
部分引用跟全部引用

```javascript=
// CountClass.js
import React from 'react'
// 也可以寫成import React, {components} from 'react'

class CountClass extends React.Components {
    ...
}
//改成class CountClass extends Components{...}

// CountFuncs.js
import {useState} from 'react'
// 一樣的~import React, {useState} from 'react'

function CountFunc(){
    const [total, setTotal] = useState(0)
}

// 也可以改成
import React from 'react'

function CountFunc(){
    const[total, setTotal] = React.useState(0)
}
```
### 二、SelectBox
* 解釋setState是非同步，會較晚執行這件事情
* 解決方法是塞到一個變數，其他需要用的就帶這個變數

```javascript=
// App.js
import SelectBox from './components/SelectBox'

function App(){
    return(
    <>
        <SelectBox />
    </>
    )
    
export default SelectBox
```

```javascript=
// components/SelectBox.js
import React, {useState} from 'react'

function SelectBox(){
    const [text, setText] = useState(0)
    
    // // 這二行const這樣會有問題
    // const handleChange = (e) =>{
    //     setText(e.target.value) //後執行
    //     console.log(text) //先執行
    // }
    
    //解決上面setState是非同步的問題
    const handleChange = (e) =>{
        
        //先計算最後的值，塞到一個變數，再用他來做事情
        const newText = e.target.value
        
        //給react呈現畫面用
        setText(newText) //後執行
        
        //做別的事情，像是跟資料庫連動
        console.log(newText) //先執行
    }
    
    
    return(
    <>
        <select onChange={this.handleChange} 
                value={this.state.value}>
            <option value="JavaScript" key={1}>
                JavaScript
            </option>
            <option value="Angular2" key={2}>
                Angular2
            </option>
            <option value = "React" key={3}>
                React
            </option>
        </select>
        <h1>{text}</h1>
    </>
    )
}

```