---
# System prepended metadata

title: React(MRWR)第 3 節： Building with Reusable Component
tags: [React, Udemy]

---


> Udemy課程：[Modern React with Redux [2023 Update]](https://www.udemy.com/course/react-redux/)

## 3-24. Project Overview
重新來複習一下如何建置一個React專案。
<div style="background-color: #deeba2; color: #4b570e; margin: 10px 0; padding: 10px 20px; width: fit-content;">
Step 1.
</div>

利用Node.js的套件管理工具npm進行安裝React的環境。首先在電腦桌面打開終端機（terminal）（亦即終端機目前所在檔案位置為桌面），並且於終端機中輸入：
<pre class="lang-js s-code-block">
npx create-react-app pdas
</pre>
    
此時npm便會幫你安裝且建置好一個React專案"pdas"。
PS "pdas"為專案名稱，可自行改成任意名稱。

<div style="background-color: #deeba2; color: #4b570e; margin: 10px 0; padding: 10px 20px; width: fit-content;">
Step 2.
</div>

利用cd進入到"pdas"的檔案位置。
<pre class="lang-js s-code-block">
cd pdas
</pre>


<div style="background-color: #deeba2; color: #4b570e; margin: 10px 0; padding: 10px 20px; width: fit-content;">
Step 3.
</div>

最後在終端機打上`npm start`，便可以透過http://localhost:3000（預設為3000）打開"my-app"專案：
<pre class="lang-js s-code-block">
npm start
</pre>

當有跳出React的網頁（如以下畫面）即成功建置React專案！

（PS 若想離開、關掉，則在終端機按下`ctrl`+`c`）

****

## 3-25. Creating Core Components
首先，先做些基礎的setup，把`src`資料夾中原先的所有預設檔案都給刪除。

![](https://hackmd.io/_uploads/rka_k8D52.png)

接著，重新建立新的三個檔案。

![](https://hackmd.io/_uploads/Hkj0JUv9h.png)

可以注意新的檔案中的App.js以及ProfileCard.js用了大寫開頭，這是因為如第二章提過得，用於建立component的檔名，在開發上習慣運用大寫作為開頭。

****

## 3-26. Introducing the Props System
第三章課程專案的markup如下圖：
![](https://hackmd.io/_uploads/BkcXFwP9h.png)


可知其中ProfileCard component各自擁有不同的內容、相同的架構。三個ProfilCard component的架構都有圖片、title、twiter handle、介紹，而各個內容皆不相同（例如title分別為Alexa、Cortana、Siri）。

目前什麼都還沒做時的程式樣子：
![](https://hackmd.io/_uploads/rkjk9vv9n.png)

結構如下圖所示：
![](https://hackmd.io/_uploads/ByLP_wwqh.png)

我們在這個範例中，稱`<App />`為parent component，`<ProfileCard />`為child component。

而要將parent component及child component聯繫在一起，就需要透過props system，利用props system，便可以為child component客製化想要的內容，並且將內容回傳給parent component。
![](https://hackmd.io/_uploads/Bypi9Pvqh.png)

我們可以從下圖給的細項，更清楚整體架構。
![](https://hackmd.io/_uploads/SkuB2wwq2.png)

大致可以4個步驟：
1. 在parent component中在JSX裡加上屬性（以上圖為例，則是新增屬性color為red）
2. React將會收集我們新增的屬性（color="red"），並將屬性放進一個object。
    所以現在我有了一個porps object存放著color為red：
    ```javascript=
    {color: "red"}
    ```
3. 接著會把props object作為引數（argument）丟進child componet function。
4. 最我們便能在我們希望利用屬性（color）的地方放上props。

:::success
**補充：argument VS parameter**

引數 (Argument) 是用於呼叫函式，
參數 (Parameter) 是方法簽章 (方法的宣告)。

e.g.
```javascript=
function test(parameter1, parameter2){
    ......
}
    
test(argument1, argument2);
```
:::

****

## 3-27. Picturing the Movement of Data
在這個章節將會依照上個章節的步驟一步步實作。

<div style="background-color: #deeba2; color: #4b570e; margin: 10px 0; padding: 10px 20px; width: fit-content;">
Step 1.
</div>

在parent component中在JSX裡加上attribute（屬性）。

首先，再次複習一下這章節完成專案後的樣子。
![](https://hackmd.io/_uploads/BkcXFwP9h.png)

因此這裡我們設立兩個attribute，分別叫做title與handle（當然這裡的attribute 的key name可以依照你喜歡的方式取名）。
```javascript=
//App.js
import ProfileCard from "./ProfileCard";

function App(){
    return(
    <div>
        <div>Personal Digital Assistance</div>

        <ProfileCard title="Alexa" handle="@alexa99"/>
        <ProfileCard title="Cortana" handle="@cortana72"/>
        <ProfileCard title="Siri" handle="@siri44"/>
    </div>
    )
}

export default App;
```
![](https://hackmd.io/_uploads/S1ooSdD9n.png)

<div style="background-color: #deeba2; color: #4b570e; margin: 10px 0; padding: 10px 20px; width: fit-content;">
Step 2.
</div>

React將會收集我們新增的attributes（屬性），並將屬性放進一個object。(如下圖）

![](https://hackmd.io/_uploads/ByjtO_vq2.jpg)

<div style="background-color: #deeba2; color: #4b570e; margin: 10px 0; padding: 10px 20px; width: fit-content;">
Step 3.
</div>

接著會把props object作為引數（argument）丟進child componet function的參數props中。

```javascript=
//ProfileCard.js

function Profilecard(props){
    ......
}
```

<div style="background-color: #deeba2; color: #4b570e; margin: 10px 0; padding: 10px 20px; width: fit-content;">
Step 4.
</div>

最我們便能在我們希望利用屬性的地方放上props。
```javascript=
//ProfileCard.js
function ProfileCard(props){
    return(
        <div>
            <div>Title is {props.title}</div>
            <div>Handle is {props.handle}</div>
        </div>
    )
}

export default ProfileCard;
```

![](https://hackmd.io/_uploads/rJHNcdw92.png)

總結來說，就是props object作為引數（argument）丟進child componet function的參數props中，之後利用props的keys取得props的value，讓value show在螢幕上。
![](https://hackmd.io/_uploads/BkbncODqh.jpg)

<div style="border: black 1px solid; padding: 5px 5px; margin: 15px 0px; display: flex; align-items: center; justify-content: center;">
    <h3 style="margin: 0;">統整</h3>
</div>

![](https://hackmd.io/_uploads/S1Ko0_Dch.jpg)

****

## 3-29. Using Argument Destructuring
不過出現了一點小麻煩，假如我們在ProfileCard.js中的內容變成如底下這樣複雜：
```javascript=
//ProfileCard.js
function ProfileCard(props){
    return(
        <div>
            <div>Title is {props.title}</div>
            <div>Handle is {props.handle}</div>
            <div>Title is {props.title}</div>
            <div>Handle is {props.handle}</div>
            <div>Title is {props.title}</div>
            <div>Handle is {props.handle}</div>
            <div>Title is {props.title}</div>
            <div>Handle is {props.handle}</div>
        </div>
    )
}

export default ProfileCard;
```

一堆參數props在JSX中非常的礙眼，那我們是否有辦法讓程式變得更易讀？

其實方法相當簡單，就是在function中先宣告變數即可：
```javascript=
function ProfileCard(props){
    const title = props.title;
    const handle = props.handle;
    
    return(
        <div>
            <div>Title is {title}</div>
            <div>Handle is {handle}</div>
            <div>Title is {title}</div>
            <div>Handle is {handle}</div>
            <div>Title is {title}</div>
            <div>Handle is {handle}</div>
            <div>Title is {title}</div>
            <div>Handle is {handle}</div>
        </div>
    )
}

export default ProfileCard;
```

不過作為交換（trade-off），我們這裡運用了額外的兩行程式來達成目的：
```javascript=
const title = props.title;
const handle = props.handle;
```

所以這裡我們還可以藉由JS的特點之一叫做「解構（Destructuring）」，來達到縮短（condense）的動作，寫法如下：
```javascript=
const {title, handle} = props;
```

![](https://hackmd.io/_uploads/H1Gqptw9h.jpg)

不過看到上方，不知道有沒有發現"props"重複出現了！因此我們直接把`{title, handle}`帶回參數props，直接取代掉props。如此一來，當我們想將props.title或props.handle利用title與handle替換掉時，也不需要在額外增加程式碼了。
![](https://hackmd.io/_uploads/SJqK0Kvch.jpg)

最終結果：
```javascript=
function ProfileCard({title, handle}}){
    return(
        <div>
            <div>Title is {title}</div>
            <div>Handle is {handle}</div>
            <div>Title is {title}</div>
            <div>Handle is {handle}</div>
            <div>Title is {title}</div>
            <div>Handle is {handle}</div>
            <div>Title is {title}</div>
            <div>Handle is {handle}</div>
        </div>
    )
}

export default ProfileCard;
```

****

## 3-31. The React Developer Tools
這章節老師要介紹免費的react tool：[React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi)

![](https://hackmd.io/_uploads/SkKH-cP5n.png)

當你加入到chrome之後，再次從瀏覽器打開你的react專案，按下「檢查」（或者是F12），可以看到上方工具列多了兩個選項："Component"跟"Profiler"。

點開"Component"，可以看見你所建立的所有components，且它將組織成tree format，所以當你觸碰各個元件，可以更清楚他們各自屬於哪個部份。而最右側則還能看見各元件的細項（例如：keys and values）

![](https://hackmd.io/_uploads/H1OfGcP9n.png)

****

## 3-34. Including Images
這章節要加入圖片進專案。首先，利用老師給予的三張圖片放入`src`資料夾之中。

![](https://hackmd.io/_uploads/B1lfCdqqn.png)

接著利用前面提到過的import方法，在`App.js`中import這些圖片。

![](https://hackmd.io/_uploads/HJSnROq53.png)

不過AlexaImage、CortanaImage、SiriImage這三個變數代表的是什麼東西？這裡我們利用`console.log()`來一探究竟。

![](https://hackmd.io/_uploads/rkRtlF9c2.png)

接著到瀏覽器看看印出了什麼東西？

![](https://hackmd.io/_uploads/Hk12lY9ch.png)

會發現AlexaImage、SiriImage印出的東西不一樣！這裡老師提到，會有這樣的差異，主要原因是因為圖片的大小關係。

我們先看看AlexaImage、SiriImage兩張圖片的大小
![](https://hackmd.io/_uploads/HkDu_Ycq2.png)
AlexaImage圖片小於9.7kb，至於SiriImage圖片則大於9.7kb。

老師提到當圖片檔案小於9.7kb時，那麼server將會抓取圖片的RAW資料，該圖片會被轉成一種格式叫做base64，我們看到`console.log(AlexaImage)`後印出的那一連串String即表示了整張圖片，也就是說它將圖片的所有像素、顏色，任何關於圖片的東西全部變成了一個字串後，將這個字串丟給JS檔案。

而如果圖片大於9.7kb，它會以額外的檔案做處理，也就是給予瀏覽器「檔案路徑」，讓瀏覽器透過檔案路徑去找到該圖片。

這就是為什麼`console.log()`兩張圖片後，會得到不一樣的結果。

![](https://hackmd.io/_uploads/SklTOY9c2.png)

（但我在網路上實在找不到上面提到的相關文件QQ所以跑去問了助教）

:::warning
Q:
I want to know that in section 34 of the course, around 3:56, it is mentioned that the output from console.log differs based on the size of the image. I'd like to ask the instructor if there are any official documents or references available for this part? I couldn't find relevant information online.

A:
<div data-purpose="safely-set-inner-html:rich-text-viewer:html" class="ud-text-sm rt-scaffolding"><p>This is a Webpack configuration that is implemented by Create React App:</p><p><em>To reduce the number of requests to the server, importing images that are less than 10,000 bytes returns a </em><a target="_blank" rel="noopener noreferrer" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs"><em>data URI</em></a><em> instead of a path. This applies to the following file extensions: bmp, gif, jpg, jpeg, and png. SVG files are excluded due to </em><a target="_blank" rel="noopener noreferrer" href="https://github.com/facebook/create-react-app/issues/1153"><em>#1153</em></a><em>. You can control the 10,000 byte threshold by setting the </em><code><em>IMAGE_INLINE_SIZE_LIMIT</em></code><em> environment variable as documented in our </em><a target="_blank" rel="noopener noreferrer" href="https://create-react-app.dev/docs/advanced-configuration"><em>advanced configuration</em></a><em>.</em></p><p><a target="_blank" rel="noopener noreferrer" href="https://create-react-app.dev/docs/adding-images-fonts-and-files/">https://create-react-app.dev/docs/adding-images-fonts-and-files/</a></p></div>

![](https://hackmd.io/_uploads/ByfrRxac2.png)

感謝助教QAQ
:::

好的，那知道AlexaImage、CortanaImage、SiriImage這三個變數代表著什麼之後，我們就要運用在JSX中了（寫法如下所示）。

```javascript=
<img src={AlexaImage} />
<img src={CortanaImage} />
<img src={SiriImage} />
```

![](https://hackmd.io/_uploads/ryOnjt5q3.png)

而我們的網頁變化如何呢？結果是沒有問題的。
![](https://hackmd.io/_uploads/rkPWnt9c2.png)

也就是說Browser可以處理Base64亦或是提供檔案路徑的圖片。

****

## 3-35. Handling Image Accessibility
上一章講完圖片的運作方式後，這章節將要運用props來加入圖片。

先再次複習一下3-26節的內容。
:::info
![](https://hackmd.io/_uploads/Bypi9Pvqh.png)

我們可以從下圖給的細項，更清楚整體架構。
![](https://hackmd.io/_uploads/SkuB2wwq2.png)

大致可以4個步驟：
1. 在parent component中在JSX裡加上屬性（以上圖為例，則是新增屬性color為red）
2. React將會收集我們新增的屬性（color="red"），並將屬性放進一個object。
    所以現在我有了一個porps object存放著color為red：
    ```javascript=
    {color: "red"}
    ```
3. 接著會把props object作為引數（argument）丟進child componet function。
4. 最我們便能在我們希望利用屬性（color）的地方放上props。
:::

所以依照上方步驟，來一步步加入圖片。
<div style="background-color: #deeba2; color: #4b570e; margin: 10px 0; padding: 10px 20px; width: fit-content;">
Step 1.
</div>

在parent component中的JSX加入屬性，這裡的parent component是`App.js`中的`App`，而我們想加入的屬性就命名為`image`(可任意命名)，而賦予`image`的值因為是變數，所以記得用curly braces包起來。

**App.js**
![](https://hackmd.io/_uploads/Hk5RJc59h.png)

<div style="background-color: #deeba2; color: #4b570e; margin: 10px 0; padding: 10px 20px; width: fit-content;">
Step 2.
</div>

此時，我們的props object就多了三個新的屬性：
```javascript=
{image: {AlexaImage}}
{image: {CortanaImage}}
{image: {SiriImage}}
```

那這裡老師有提到說，若我們在parent component中加了屬性，之後卻沒在child component中使用，這是沒有關係的，不會造成錯誤。

<div style="background-color: #deeba2; color: #4b570e; margin: 10px 0; padding: 10px 20px; width: fit-content;">
Step 3.
</div>

接著props object會作為引數（argument）傳入到child component function（這裡的child componet即ProfileCard）中。

記得，child component function的參數（parameter）中加上`image`，也就是我們即將傳入的引數名稱。

切記！引數及參數名稱要一樣！這樣props object才能對應到符合的key & value。

**ProfileCard.js**
![](https://hackmd.io/_uploads/r1voMccqh.png)

****

## 3-36. Review on how CSS Works
這章節會添加CSS樣式，有兩種方式完成CSS，一種是自己客製化（自己寫），另一種是利用library。
![](https://hackmd.io/_uploads/S1Ue2ii92.png)


老師介紹了一個CSS函式庫：[Bulma](https://bulma.io/)
他可以不需要知道太多CSS相關的知識，就能讓我們的網頁變得比較好看些。

先從文件中找到自己想要添加的內容。
![](https://hackmd.io/_uploads/B1wxojs92.png)

****

## 3-37. Adding CSS Libraries with NPM
上節介紹了Bulma的用途，這章節將從安裝開始。這裡將採用在terminal上，利用npm來安裝bulma。
```
npm install bulma
```

![](https://hackmd.io/_uploads/B1wPaji53.png)

安裝完之後，我們便可以在該專案的`node_modules`找到bulma
![](https://hackmd.io/_uploads/ByCES2i92.png)

因此這個時候我們就可以在JS檔案中import bulma來做使用。

這裡可以參考[2-22](https://hackmd.io/fxY8uO0fTpa9XBizBKTYWg?view#import-from-)提到的「import後面from哪裡，不同來源會有不同寫法，例如說如果檔案室我們自己建立的（例如App.js），則import時會有"./“或”…/“，若我們import的東西是來自package（即一開始npm 下載的各種資源），前不會有”./“或”…/"。」

所以我們想要import bulma的話，只要打上以下內容即可。
```javascript=
import "bulma/css/bulma.css";
```

可以記得這樣的方法，因為大多數的官方文件並不會寫出這個方法。

****

## 3-38. A Big Pile of HTML!

1. 這裡有個要注意的點，在Bulma文件中給予的範例，都是以HTML為主，不過我們要寫在JSX中，所以要記得將HTML轉換成JSX的寫法。（可以複習[2-18、2-19](https://hackmd.io/@noz915/H1qCybZq2#2-18-Converting-HTML-to-JSX)）

2. 單張card完成之後，要注意多張card沒有並列的問題。可以從Bulma官方文件：[columns layou](https://bulma.io/documentation/columns/basics/)部份得到解答。

    多張card要並列，需要至parent component（App.js檔案）做修改。