# React 入門 1 - Element, Function Component, & props


接下來要認識 Components。

Component 在 React 裡面分為 **Function Component** 和 **Class Component**，我目前的規畫是先熟悉Function Component的寫法，再去瞭解Class Components，會這樣規劃是基於以下幾個原因：
1. 雖然以前大家都是用Class Component，但[React 在16.8版推出Hook](https://reactjs.org/docs/hooks-intro.html)以後，現在專案開發的趨勢是使用 Function Component 這種比較簡潔的寫法；
2. 就我所知，學習 Class Component 需要認識跟生命週期(Lifetime)有關的方法、比較複雜，所以先從Function Components下手；
3. 考慮到舊專案可能還在用Class Component，所以之後還是有必要學習!

不過在認識Component以前，還是先快速認識一下React語法裡面真正的最小組成單位，Element。

-----
## Element
React對於Element的解釋是這樣子的：
1. Element是React App的最小組成單位；
2. 任何Element本質上都是一個JavaScript的物件。

所以，具體來說什麼是Element？假設現在先用純HTML語法寫出一個具有class屬性的h1標籤，語法如下：
```
<h1 class="redTitle">Hello!</h1>
```
在JavaScript操作DOM時，通常稱呼這個h1標籤為 *object(物件)*；在React裡則稱為 ***element***。

若要使用React語法創造這樣的h1元素，[必須使用 `React.createElement()` 方法](https://reactjs.org/docs/react-without-jsx.html)，改成下面這樣的語法：
```
const element = React.createElement("h1",{className: "redTitle", "Hello!"}）;

// 會產生像下面這樣的物件
{
    "h1",             // tag name
    "redTitle",       // class (HTML attribute),
    "Hello!"          // content
}
```

想像一下，如果每一個element都像這樣寫，在開發時反而變得更複雜，也不容易除錯，因此[第0篇](https://urlun0404.coderbridge.io/2022/03/21/react-%E8%B5%B7%E6%89%8B%E5%BC%8F-0-%E7%92%B0%E5%A2%83%E6%9E%B6%E8%A8%AD/)時有提到，React提供類似HTML語法的擴充語法**[JSX](https://reactjs.org/docs/introducing-jsx.html "Introducing JSX – React")**，讓我們可以便利快速地去產生element，所以用JSX語法改寫上面的Element：
```
const element = <h1 className="redTitle">Hello!</h1>;
```
這裡唯一要注意的是，原來HTML的class屬性要記得改成 **className**（因為class是JavaScript的關鍵字）。
<br/>

-----
## Function Component and props
這邊並只先專注於介紹Function Component，同時也介紹Component的重要性質－props。

**Component**(元件)是指至少一個以上Element組成的獨立物件，Components之間可以彼此互不干擾地運作，也能多個Components組合起來形成網頁介面，總之使用Component有以下幾個性質：
1. Components可以各自獨立更新：意思是若網頁裡有好幾個Components，若只有其中一個Component的值改變，可以只更新這個Component；

2. 前面所提到的Element可以是原生的DOM物件 `<div></div>`、`<img/>`等，也能是一個自製Component (User-Defined Component)，例如 Function Component 和 Class Component。這裡要注意的是，為了區隔原生DOM物件和自製Component，自製Component都要以大寫開頭，例如：
```
const nativeDomElement = <div>這是原生DOM物件所產生的Element</div>;
const userDefinedElement = <Hello />;    // 這個Element是一個名為Hello的自製Component
```

3. React官方建議太過複雜的JSX語法可以[拆成多個Component再去組合](https://reactjs.org/docs/components-and-props.html#extracting-components)；

4. Component可以擁有自己的屬性(properties)，在React稱為 **props**。
<br/>

#### Function Component
Function Component顧名思義就是用JavaScript function 定義一個Component。

例如下方程式碼就是定義一個跟成績有關、名為 *Grade* 的function component，最後使用 `ReactDOM.render()` 方法將這個Component渲染到網頁畫面上。
```
// function component
function Grade(){
    return <div>Math</div>;
}

// Render
ReactDOM.render(<Grade/>, getElementById('root'));
```
<br/>

#### props
前面有提到Component可以有自己的屬性(properties)，在React裡面這些屬性構成一個名為 **props** 的屬性物件。props的語法有點像是HTML的attribute，但你可以根據需求自行決定這個Component擁有哪些props？並且將props傳遞到Component裡面去處理。

先用前面的Grade Component舉個例子，假設今天希望讓學生自行輸入數學成績，可以透過props去傳遞學生所輸入的值，再動態地顯示在畫面上，範例如下：
```
// function component
function Grade(props){
    return (
        <div>
          <h1>{props.subject}</h1>
          <h3>Your {props.subject} grade is {props.grade} point!</h3>
        </div>
    );
}

// Render
ReactDOM.render( <Grade subject="Math" grade={prompt("Enter your Math grade:")} />
    ,
    getElementById('root')
);
```
首先，我們用類似HTML attribute的語法，在Grade Component 寫上想要傳遞的 props，如 `subject="Math"` 和 `grade={prompt("Enter your Math grade:")}`。

注意這裡的grade屬性用到的是JavaScript的prompt方法，所以**用大括號 `{}` 包住JS語法，告訴JSX要寫入的是JS語法**，而不是像`"Math"` 單純的字串(string)。

接著將props物件傳遞到 `fucntion Grade(props){...}`，並且同樣以JavaScript取得物件屬性值的語法寫下 `{props.subject}` 獲得屬性值。這裡要特別注意的是，`return` 只能回傳一個值，但今天希望同時回傳科目名稱(props.subject)和成績(subject.grade)，就必須使用一組`<div></div>`（或者改以一組 `[<React.Fragement></React.Fragement>](https://reactjs.org/docs/fragments.html)` 或 `<></>`）去包住其他元素（`<h1>`和`<h3>`）。
<br/>

*如果希望學生一次國文、數學和英文成績，我們能藉由Component可以重複利用的性質，重複使用Grade Component，讓學生輸入三個科目的成績再回傳呢？*
<br/>

這裡我們特別再做一個Student Component去整理代表三個科目的三個 Grade Components，並且渲染Student Component，程式碼如下：
```
// function component
function Grade(props){
    return (
        <div>
          <h1>{props.subject}</h1>
          <h3>Your {props.subject} grade is {props.grade} point!</h3>
        </div>
    );
}

function Student(props){
    return(
        <div>
            <Grade subject="Math" grade={props.math}/>
            <Grade subject="Chinese" grade={props.chinese}/>
            <Grade subject="English" grade={props.english}/>
        </div>
    );
}

// Render
ReactDOM.render(
    <Student
      chinese={prompt("Enter your Chinese grade:")}
      math={prompt("Enter your Math grade:")}
      english={prompt("Enter your English grade:")}
    />
    ,
    getElementById('root')
);
```

附上線上程式碼執行結果：
[Live Demo](https://codesandbox.io/s/react-beginner-e8oc7d?file=/src/index.js)
<br/>

最後補充 props 的值僅供讀取 **(Read-Only)**，傳遞後不能修改，例如下面程式碼就是錯的：
```
function Sum(props){
    return props.total -= props.subtraction;
}
```
但網頁常常需要一些可讓使用者改變的效果，如果希望改變元素值的話，則要使用到**state**。

-----
## 重點整理
1. Function Component 的 `return` 只能回傳一個Element；要回傳多個Elements，必須用一組Element（常用`<div></div>`、或 `<React.Fragement></React.Fragement>` 或 `<></>`）包住要多個Elements再回傳，例如 `return (<div> <h1></h1><img/> </div>)` 或是  `return (<> <h1></h1><img/> </>)`。
2. Component 可以重複利用、相互組合，因此太過複雜的語法或Element建議分成多個Component再組合。
3. Component 可以有自己的屬性－**props**，props 能夠動態傳遞屬性值到Component裡面作處理或顯示在畫面上。
4. props 不能改變傳遞的屬性值，若要改變值則必須用到 **state**。
<br/>

-----
**補充：<Fragement\>**
前面有提到function component只能回傳一組JSX標籤，原因在於function component實際是一個函式(function)，函式只能回傳一個值；誠如前面所說，function component所回傳的JSX標籤語法其實也是回傳一個物件的函式：
```
<h1 class="redTitle">Hello!</h1>

// i.e., React.createElement("h1",{className: "redTitle", "Hello!"}）
// return {"h1", "redTitle", "Hello!" }
```
若function component回傳兩個JSX標籤即代表會回傳兩個值：
```
return (<h1 class="redTitle">Hello!</h1><h3>World!</h3>)

/*
 *  return (
 *      React.createElement("h1",{className: "redTitle", "Hello!"});
 *      React.createElement("h3",{null, "World!"});
 *  );
 */
```

由此可知這並不符合JavaScript語法的規範，所以才必須再用一組JSX標籤包住回傳：
```
return (
<div>
    <h1 class="redTitle">Hello!</h1>
    <h3>World!</h3>
</div>
);


/*
 *  return (
 *      React.createElement("div",{null, {
 *          React.createElement("h1", {className: "redTitle", "Hello!"}),
 *          React.createElement("h3",{null, "World!"})
 *      });
 *  );
 */
```
但因為Component可以包住另一個component，若一個component內有另一個component，而這個component內又有另一個component；若為了回傳一個值而使用 `<div></div>`，解析成HTML很可能會變成如下面的狀況，導致重要資訊被多個不重要 `<div></div>` 包住：
```
<div>
    <div>
        <h1 class="redTitle">Hello!</h1>
        <h3>World!</h3>
        <div>
            <p>...</p>
        </div>
    </div>
    <div>
        <div>
            <p>...</p>
        </div>
    </div>
</div>
```
在網頁語意上， `<h1>` 代表重要標題，而 `<div>` 標籤無法傳達任何重要資訊，若 `<h1>` 的外圍被多個 `<div>` 包住，造成網頁搜尋引擎不能從網頁第一層看到重要訊息 `<h1>` ，就會降低網頁的SEO。

若將 `<div></div>` 改成 `<Fragement></Fragement>`標籤（或用簡寫 `<></>`）：
```
return (<>
    <h1 class="redTitle">Hello!</h1>
    <h3>World!</h3>
    <>
        <p>...</p>
    </>
</>);
```

則會解析成：
```
<h1 class="redTitle">Hello!</h1>
<h3>World!</h3>
<p>...</p>
```
如此一來就可以避免重要的語意標籤外圍有好幾層的 `<div></div>` 標籤，同時增強網頁的SEO。
<br/>

-----
References
- [React官方文件](https://reactjs.org/docs/hello-world.html) 
- [Fragments @React Docs](https://reactjs.org/docs/fragments.html) 
- [Wilson Ren - 2022網頁開發全攻略(HTML, CSS, JavaScript, React, SQL, Node, more)](https://www.udemy.com/course/html5-css3-z/)
