# 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> 是一行
```
* 箭頭函式和一般函式的差別

* 箭頭函式沒有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會自動檢查開發的元件是不是有問題。可寫可不寫。
### 3. 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>
</>
)
}
```