---
# System prepended metadata

title: 1.25 React
tags: [React]

---

# 1.25 React
###### tags: `React`
## 零、目錄&待辦事項
[TOC]

## 壹、元件的狀態&屬性複習

## 貳、元件溝通方式

### 一、父母向子女 -> 塞在props給子女
開檢查工具看Component：
App > Parent > Child
```javascript=
// App.js
// App.js 接下來都相同
import Parent from './components/Parent'
function App(){
    return
    <>
        <Parent />
    </>
}

export default App

// Parent.js
import Child from './Child'
function Parent(){
    const innerData = '父母元件內部資料'
    return <Child data={innerData}/>
}

export default Parent

//Child.js
function Child(props){
    // 利用props得到由父母元件傳遞過來的資料
    return <h1>{props.data}</h1>
}

export default Child
```
### 二、子女向父母 -> 父母給子女function，子女塞在function的參數，送回給父母
React是單向資料流，沒辦法直接子女傳向父母，要用間接的方式才可。
App > Parent > Child

```javascript=
// Parent.js
import Child from './Child'
import {useState} from 'react'
function Parent(){
    const [data, setData] = useState('')
    return (
        <>
            <h1>{data}</h1>
            <Child setData = {setData} />
            // 把父母元件的函式傳給子女，式子女透過這個函式設定data
            // 前面的setData是Child的屬性(?)
            // 後面的{setData}是解構來的函式，會做設定狀態的動作，他會作為props傳給子女，子女把參數就進去，傳回給父母得到data值
            
            
        </>
    )
}

export default Parent

// Child.js
function Child(props){
    const innerData = '子女元件內部資料'
    return (
        <button onClick={()=>{
            // 利用props得到的setData函式來設定資料給父母元件
            props.setData(innerData)
        }}>
            送資料給父母元件
        </button>
    )
}

export default Child
```
### 三、子女對子女，一、二的綜合型，子女一先塞在funciton給父母，父母得到後塞在props給子女二
App > Parent > ChildOne、ChildTwo

要透過父母才能傳遞資料
One送給Parent，Parent再送給Two
```javascript=
// Parent.js
import ChildOne from './ChildOne'
import ChildTwo from './ChildTwo'
import {useState} from 'react'

function Parent(){
    const [data, setData] = useState('')
    return (
        <>
        // One設定資料給Parent、Parent傳給Two
            <ChildOne setData = {setData} />
            <ChildTwo data = {data} />
        </>
    )
}

export default Parent

// ChildOne.js
function ChildOne(props){
    const innerData = 'ChildOne子女元件內部資料'
    return (
        <button onClick={()=>{
            props.setData(innerData)
        }}>
            送資料給ChildTwo元件
        </button>
    )
}

export default ChildOne

// ChildTwo.js
function ChildTwo(props){
    const innerData = '子女元件內部資料'
    return (
        <h1>{props.data}</h1>
        
    )
}

export default ChildTwo

```

### 四、任何元件互傳
* 用第三方函式庫或用鉤子(react context api)
* react context api是個用來傳遞資料的鉤子，但要搭配useContext、useReducer使用，初學者太難。

### 五、實例-4.以props作為狀態的初始值 (AntiPattern)
* 利用props來設定狀態的初始，容易造成執行上錯誤
* 解決方法：不要讓子女元件有狀態，其狀態是來自於父母
* 這裡的解法也不是好解法，之後要用生命週期方法來解決
* App > CountParent > CountFunc
```javascript=
// App.js
import CountParent from './components/CountParent'

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

export default App
```
```javascript=
// components/CountParent.js
import { useState } from 'react'
import CountFunc from './CountFunc'

function CountParent() {
  // 使用狀態
  const [initNumber, setInitNumber] = useState(0)

  return (
    <>
      <CountFunc initNumber={initNumber} />
      <button
        onClick={() => {
          setInitNumber(10)
        }}
      >
        設定一開始為10
      </button>
      <button
        onClick={() => {
          setInitNumber(100)
        }}
      >
        設定一開始為100
      </button>
    </>
  )
}

export default CountParent
```
```javascript=
// components/CountFunc.js
import React, { useState } from 'react'

function CountFunc(props) {
  // const [total, setTotal] = useState(props.initNumber)
  // 利用props做為狀態的初始值，這種反模式antipattern會造成錯誤
  const [total, setTotal] = useState(0)

  return (
    <>
      // 這行會是錯的
      // <h1>{total}</h1>
      <h1>{props.initNumber + total}</h1>
      <button
        onClick={() => {
          setTotal(total + 1)
        }}
      >
        +1
      </button>
      <button
        onClick={() => {
          setTotal(total - 1)
        }}
      >
        -1
      </button>
    </>
  )
}

export default CountFunc
```

### 六、實例-5.以props作為狀態的初始值-利用父母元件的state
* 子女元件一開始不要有狀態，由父母元件的狀態傳給子女作為初始值

```javascript=
// components/CountParent.js
import { useState } from 'react'
import CountFunc from './CountFunc'

function CountParent() {
  // 使用狀態
  const [total, setTotal] = useState(0)

  return (
    <>
      <CountFunc initNumber={initNumber} total={total} setTotal={setTotal}/>
      <button
        onClick={() => {
          setTotal(10)
        }}
      >
        重設定一開始為10
      </button>
      <button
        onClick={() => {
          setTotal(100)
        }}
      >
        重設定一開始為100
      </button>
    </>
  )
}

export default CountParent
```
```javascript=
// components/CountFunc.js
import React, { useState } from 'react'

function CountFunc(props) {
  // 不要讓子女元件有狀態，子女元件的狀態要來自於父母
  // const [total, setTotal] = useState(0)
  const {total, setTotal} = props

  return (
    <>
      <h1>{total}</h1>
      <button
        onClick={() => {
          setTotal(total + 1)
        }}
      >
        +1
      </button>
      <button
        onClick={() => {
          setTotal(total - 1)
        }}
      >
        -1
      </button>
    </>
  )
}

export default CountFunc
```
## 參、生命週期

[講義在這裡](https://github.com/eyesofkids/mfee11-react/blob/main/%E6%95%99%E6%9D%90/0121/react%E6%8A%95%E5%BD%B1%E7%89%87-%E7%8B%80%E6%85%8B-%E5%B1%AC%E6%80%A7-%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.pdf)

[有個圖形解說的在這裡](https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/)

### 一、生命週期方法：掛載、更新、卸載
* 生命週期方法只有類別型元件有，函式型元件式有類似概念的方法

#### 1. 掛載 mounting

* 從無到有，程式已經運算好了，畫面尚未呈現。
* 從無到有的過程：
    * 虛擬dom程式碼
    * 經由ReactDom render產生
    * constructor 類別型元件建構(雖然沒有建構式)
        * 元件初始化
    * ~~getDrivedStateFromProps 從屬性得到延伸的狀態~~(用來做程式的最佳化，跳過)
    * render 類別的render、就像函式型元件的回傳值
        * 元件呈現到網頁上
    * componentDidMount 元件已經掛載，這個是最重要的
        * react的虛擬dom已經真正呈現在網頁上，和一般的dom無異，可以和其他JS、JQ等程式整合了
    * constructor、render、componentDidMount是重點
    * constructor > render > componentDidMount

#### 2. 更新 Updating
已經存在後，更新內容，重新渲染，網頁重新呈現
1. 更新內容
    * react元件更新只有3種方式
        1. setState 按了這個按鈕後用setState去做重新設定
        2. New Props 從父母元件收到新的設定值
        3. ~~forceUpdate 這也是最佳化的部分~~，跳過
2. render產生頁面
3. componentDidupdate，第二重要的生命週期方法

* ~~shouldComponentUpdate、getSnapshotBeforeUpdate，最佳化~~，跳過

* render > conmponentDidUpdate


#### 3. 卸載 Unmounting
* 從有到無
* componentWillUnmount，和掛載相對，掛載有什麼，就要卸載什麼。
* 像是有監聽就要卸載監聽、有計時器就要卸載計時器
* componentWillunmount



## 肆、實例-6.類別型元件-生命周期方法-掛載
### 生命週期方法本來就是設計給類別型元件用的

函式型沒有constructor，沒有render三方法，沒有componentDidMount，沒有componentWillUnMount，但他有自己的生命週期方法

```javascript=
// App.js

// CountClass.js
import React from 'react'
class CountClass extends React.Component{
    constructor(){
        ...
    }
    componentDidMount(){
        ...
        在這裡面才可以掛事件監聽、JS、JQ程式碼
    }
    
    componentDidUpdate(){
        ...
        setState、New props二種方法更新
    }
    
    componentWillUnmount(){
        ...
        元件消失在網頁上
        與componentDidMount相對
        componentDidMount加掛了什麼
        componentWillUnmount就是卸載調它
        要在App.js裡面多一個狀態，使元件消失時就會呼叫componentWillUnmount
    }
    render(){
        ...
    }
}

// App.js
// 卸載的話要多一個切換
import { useState } from 'react'

import CountClass from './components/CountClass'

function App() {
  const [show, setShow] = useState(true)

  return (
    <>
      {show && <CountClass />}
      <hr />
      <button
        onClick={() => {
          setShow(!show)
        }}
      >
        {show ? '消失吧' : '復活吧'}
      </button>
    </>
  )
}

export default App
```

* react內沒有if/else的語法，要用
```javascript=
1 ? 2 : 3
if (1){
    執行2
} else 3

1 && 2
if (1){
執行2
}
```

## 伍、ES6補充 Side Effects(副作用)與Pure Functions(純粹函式)

[補充講義在這裡](https://github.com/eyesofkids/mfee11-react/issues/9)
* 副作用：主要作用外，額外產生的其他作用。
    * 除了函式的執行外，可能會改變到其他或外部狀態、呼叫到其他函式。
    * 表達式或函式可能會造成副作用
    * 例如JavaScript的自動轉型，最好使用嚴格相等比較(=\==)與(!=\=)，而不要使用值相等比較==與!=，是因為要避免任何不經意的"副作用"
* 純粹函式：不會造成副作用的函式
    * 給定相同的輸入，會有相同的輸出
    * 不依賴外部狀態(外部的變數)
* 如果程式碼內有非同步、資料的輸入輸出，就是會有副作用




## 陸、Hooks
[講義在這裡](https://github.com/eyesofkids/mfee11-react/blob/main/%E6%95%99%E6%9D%90/0121/React%E5%8B%BE%E5%AD%90(hooks)/intro.pdf)
* 函式型元件可以用鉤子，類別型元件不能
* 類別型元件可以用生命週期方法，函式型元件不能
* 函式型元件會用Hooks來模擬出生命週期方法，也就是useEffect
* useEffect指的是使用副作用方法
```javascript=
// CountFunc.js

import React, {useState, useEffect} from 'react'

function Count(){
    // 沒有明確地模擬建構，只有設定狀態初始值
    const [total, setTotal] = useState(0)
    
    // 模擬出componentDidMount
    // 2個參數，第1個是CB，第2個是陣列。陣列是用來處理update的，這裡一開始可以是空的。
    useEffect(()=>{模擬mount},[])
    
    // 模擬出componentDidUpdate，但也有componentDidMount的執行過程
    // 要寫if條件式，
    useEffect(()=>{if(){
        ... }
        },[要更新的狀態或傳入props])
    
    // 模擬componentWillUnmount
    useEffect(()=>{
        retrun()=>{componentWillUnmount}
        },[])
}

useEffect(()=>{},[])
useEffect(useEffect的CB,[相依性的陣列])
相依性的陣列是要放要更新的狀態，有變化時才執行
```
## 柒、useEffect的常見用法
[在issue的這裡](https://github.com/eyesofkids/mfee11-react/issues/7)


## 捌、實例11、12-美金台幣戶轉
* react的表單元素較特別，分為可控制、不可控制
    * 可控制：React Component元件管理，推薦使用。要記得值對應到狀態、觸發事件對應到設定狀態的方法。
    * 不可控制：由實際的DOM管理
* 我們用可控的!
* 可控的表單元素有二個條件
    1. input的值對應到狀態
    2. 變動(更新)的事件對應到設定狀態的方法
* 在檢查工具中，hooks的State沒有名字，會照順序排下來而已，要自己注意
* 上傳檔案是不可控的

## 玖、範例13-多個輸入欄位(FormFields.js)
* ios的safari對於input type = date、time都不支持
* 多欄位->類別型元件，在官網的form說明文件找到多個輸入
* 函式型：要用狀態內的物件多個屬性去達成


## 拾、範例15-狀態合併機制、與函式型元件的差異

## 拾、實例：台灣郵遞區號連動式

```javascript=
// src/data/township.js
記得把這個加到data目錄



// components/ZipCode.js
import {useState} from 'react'
import {countries, townships, postcodes} from './data/townships'

function ZipCode(){
    console.log(countries, townships, postcodes)
    const [country, setCountry] = useState(-1)
    const [country, setCountry] = useState(-1)
    
    return (
        <>
            <select value={country} onChange={(e)=>{
                setCountry(+e.target.value)
                setTownship(-1)
            }}>
            <option value="-1">請選擇縣市</option>
            {countries.map((value,index)=>{
                <option key = {index} value={index}>{value}))}
                </option>
            </select>
            <select>
                <option>請選擇區域</option>
            </select>
            <h3>郵遞區號</h3>
        </>
    )
}
export default ZipCode


// App.js
import ZipCode from './components/ZipCode'

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

export default App
```


有一個按鈕，他是藍色的，點擊它後會變成紅色，再點擊則變回藍色

按鈕>元件
顏色>狀態

按鈕產生，被放到畫面上，按鈕的顏色改變了

mounting: 準備好元件
componentdidmount: 程式已經把運算完成，虛擬DOM完成，但還沒放到畫面上
updating: 更新狀態
unmounting: 把元件以及狀態消除掉
     
     
componentWillMount
componentDidMount

shouldComponentUpdate: React是透過人工的方式去判斷元件要不要改變，通常是比對現在跟下一個狀態是否不同，若不同就更新

componentWillUpdate