# React(MRWR)第 5 節: Using an API with React
#React(MRWR)第 5 節: Using an API with React
> Udemy課程:[Modern React with Redux [2023 Update]](https://www.udemy.com/course/react-redux/)
`20230802Wed.~`
:::danger
5-46 用文件查詢想獲取的API之url
5-64 axios
5-65 async、await
5-70 form預設的問題
5-71 抓取input裡資料的禁忌、方法
5-71 onSubmit、onChange都是JS中的event handler
:::
## 5-60. Project Setup
第5節專案要完成的內容如下圖,使用者可以在搜尋欄位打上關鍵字,透過API向第三方發送請求,並在底下顯示關鍵字的相關圖片們。
這次會建立4個component:
1. parent component:
* `App`:父層,最主要的檔案,用來顯示所有子層元件
2. children component:
* `SearchBar`:搜尋欄位
* `ImageList`:
用來處理所有的單張圖片,亦即用來放置`ImageShow` 元件,其為`ImageShow`的父層元件
* `ImageShow`:單張的圖片,其為`ImageList`的子層元件

下圖為所有component之間的關係:

:::warning
會發現檔案越來越多了,到後面的專案可能也會越來越複雜,若全部檔案都丟在`src`資料夾裡面,會變得相當難管理。所以我們在`src`資料夾中新增另一個`components`資料夾,將component放進該資料夾裡。
以此節專案為例則更改如下,至於`App.js`,他雖然也是component,一個有點像整個專案的中央,所以`App.js`放置在`components`資料夾裡面或外面都沒問題。

:::
****
## 5-61. The Path Forward
我們會運用到的API是由unsplash這個平台免費提供。

首先要知道React並沒有任何工具、物件或函式是可以發送HTTP請求的,React只在乎如何顯示內容以及如何處理使用者觸發的事件,但這未必是壞事,因為這代表著我們可以不用理會React去寫很多商業邏輯(business logic)或是獲取(fetching)資料的方法。
****
## 5-62. Overview of HTTP Requests
**HTTP Method**
HTTP Request 回傳的資料中含HTTP Method
> 參考資料:[HTTP 請求方法(MDN)](https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Methodshttps://developer.mozilla.org/zh-TW/docs/Web/HTTP/Methods)
**HTTP Status Code**
HTTP Response 回傳的資料中含HTTP Status Code
> 參考資料:[HTTP 狀態碼(MDN)](https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Status)
這裡要注意一點,發出請求後,會等待一小小段時間,才回獲得回應。

所以當我們寫以下第一種寫法時,JS並不會等我們獲得response,而會直接接續馬上執行下一行程式。所以我們發出請求後,應該要告訴JS:「等一下!我還沒收到回應,你先等我一回在繼續執行。」,至於是什麼語法,後面章節(5-65)會說明。

補充:JS是一種單執行緒的語言,所以他做的事情是一種同步(sync)的表現,如下圖所示。因此如果要達到上述目的,則需要運用到非同步(async)的概念。

> 參考資料:[[JavaScript] 一次搞懂同步與非同步的一切:一次做幾件事情 — 同步(Sync)與非同步(Async)](https://medium.com/itsems-frontend/javascript-sync-async-22e75e1ca1dc)
****
## 5-63. Understanding the API
接著要先來註冊API,以及看看本次專案要使用的API的文件。
<div style="border: black 1px solid; padding: 5px 5px; margin: 15px 0px">
<h3 style="margin: 0">step1.</h3>
</div>
首先,來到[Unsplash API頁面](https://unsplash.com/developers)
按下右上角的"Register as a developer"進行註冊。

<div style="border: black 1px solid; padding: 5px 5px; margin: 15px 0px">
<h3 style="margin: 0">step2. </h3>
</div>
註冊登入後,來到"Your apps"的頁面。

<div style="border: black 1px solid; padding: 5px 5px; margin: 15px 0px">
<h3 style="margin: 0">step3. </h3>
</div>
來到"Your apps"頁面後,往下滑可以看到一個區塊"New Application",並點擊進入。

<div style="border: black 1px solid; padding: 5px 5px; margin: 15px 0px">
<h3 style="margin: 0">step4.</h3>
</div>
以下皆勾選後,按下底下的"Accept terms"。

<div style="border: black 1px solid; padding: 5px 5px; margin: 15px 0px">
<h3 style="margin: 0">step5. </h3>
</div>
填寫資訊,包含名稱和描述。

<div style="border: black 1px solid; padding: 5px 5px; margin: 15px 0px">
<h3 style="margin: 0">step6. </h3>
</div>
之後會跳轉至剛剛建立的頁面中。

將該頁面下滑,可以看見區塊"keys"。

<div style="border: black 1px solid; padding: 5px 5px; margin: 15px 0px">
<h3 style="margin: 0">來看文件! </h3>
</div>
* **Location**
點擊上方的"Documentation"。並從左側列表依序進入: shema > location。

我們可以從中看到對於[Location](https://unsplash.com/documentation#location)的敘述,代表著如果我們想使用(access)API,必須發送請求到`https://api.unsplash.com/`該網址中。
```!
The API is available at https://api.unsplash.com/. Responses are sent as JSON.
```
* **Public Authentication**
另外一個值得注意的就是[Public Authentication](https://unsplash.com/documentation#public-authentication),可以從左側列表 Authorization > ublic Authentication取得資訊。
這裡提到當你想使用API,我們必須包含一個特殊的header叫做"Authentication",這個"Authentication"需要有"Client-ID YOUR_ACCESS_KEY"這些資訊。其中,"YOUR_ACCESS_KEY"為先前Step 6.產生的Keys所付資訊。
```!
To authenticate requests in this way, pass your application’s access key via the HTTP Authorization header:
Authorization: Client-ID YOUR_ACCESS_KEY
```
* **Search photos**
接著是"Search photos",可以從左側列表 Search > Search photos 找到。
其中附有參數可以參考使用。

```
Get a single page of photo results for a query.
GET /search/photos
```
****
## 5-64. Making an HTTP Request
要製作請求,在前面有提過,並不需要使用到react,一般來說會利用JS的函式庫axios或是fetch,不過老師提到這邊只會教axios(比較好用)。
課程中利用npm來安裝axios。
```!
npm install axios
```
axios 函式庫是個物件,並擁有多個function供我們使用,這些不同的function提供我們不同方法發起 HTTP Request。
例如說我們要使用function 並且傳入我們想獲取API的url作為function的第一個參數,另外第二個參數則放置一些物件(例如headers、params)。範例如下:
```javascript!
axios.get(url, {
})
```
:::info
關於第二個參數必要有的內容為header(包含Authorization)、query。

舉例來說,大致上會長這個樣子。

:::
這裡,將創建一個新的檔案叫做"api.js"
1. 在裡面import axios
2. 建立一個`searchImages`的function,並在此function中使用axios
3. 放url到第一個參數,此url即我們想獲取API的url "https://api.unsplash.com/search/photos" ==[註ㄧ]==
4. 接著,第二個參數:==[註二]==
```javascript!
headers:{
Authorization: 'Client-ID s6X6PEJrJ2RuT00Rw3HkxZotqVFvEFnQlTvQ87vnog4'
},
params:{
query: 'cars'
}
```
5. 加上async、await,並在底部return response變數
這步驟的作用將在下一章節說明。
```javascript!
import axios from "axios";
const searchImages = async () => {
const response = await axios.get('https://api.unsplash.com/search/photos', {
headers: {
Authorization: 'Client-ID s6X6PEJrJ2RuT00Rw3HkxZotqVFvEFnQlTvQ87vnog4'
},
params: {
query: 'cars'
}
})
console.log(response);
return response;
}
```
6. 加上"export default searchImages"
```javascript!
export default searchImages;
```
:::danger
[註ㄧ] API的url可以從官方文件查詢(5-63時有提到)
1. **[Location](https://unsplash.com/documentation#location)**
The API is available at
```!
https://api.unsplash.com/
```
Responses are sent as JSON.
2. **[Search photos](https://unsplash.com/documentation#search-photos)**
Get a single page of photo results for a query.
```!
GET /search/photos
```
綜合以上兩點,可得我們想要獲取API的url為:
```!
https://api.unsplash.com/search/photos
```
:::
:::danger
[註二] 第二個參數(5-63時有提到)
1. 第一個參數"header" **[Authorization](https://unsplash.com/documentation#public-authentication)**
headers放置Authorization,目的是為了告訴unsplash這個網站,現在是誰在發送這個request。
至於Authorization內容為何,同樣從文件中可得到答案:
Most actions can be performed without requiring authentication from a specific user. For example, searching, fetching, or downloading a photo does not require a user to log in.
To authenticate requests in this way, pass your application’s access key via the HTTP Authorization header:
```!
Authorization: Client-ID YOUR_ACCESS_KEY
```
其中YOUR_ACCESS_KEY即是從Keys產出的Access Key(可以從your apps查看)

2. 第二個參數"params"
params裡放置"query",而這個query為我們搜尋圖片的關鍵字,所以query會隨使用者搜尋的單詞不同而不一樣。
這裡為了示範,先用`cars`作為搜尋關鍵字,之後會再做修改。
:::
完成`api.js`檔案的內容之後,我們將它import進`index.js`檔案中並執行該專案。可以看見我們印出了以下內容:

接著展開"data" > "result"看看內容:

我們單看其中一個物件(這裡看'0'):

而這些內容,對我們而言,最重要的內容即"urls"的資料,也是這次專案會需要運用到的東西。
****
## 5-65. [Optional] Using Async:Await
在[5-62](https://hackmd.io/lE6S6Q0jRQWFBHbSc17SGg?both#5-62-Overview-of-HTTP-Requests)中曾提到一個問題,發送請求之後,需要等待一段時間才會獲得回應,而因為JS為單執行緒的語言,所以他會同步執行,也就是說還沒獲得回應,JS就會立即執行下一行程式。因此在[5-62](https://hackmd.io/lE6S6Q0jRQWFBHbSc17SGg?both#5-62-Overview-of-HTTP-Requests)我們有稍微提到發送請求後,需要告訴JS等一等,直到我們獲得回應後,才能往下執行。
這樣的動作,便需要由async、await來達成。

正確的作法實作方式:

****
## 5-66. Data Fetching Cleanup
前面完成的半成品程式碼:
**api.js**
```javascript!
import axios from "axios";
const searchImages = async () => {
const response = await axios.get('https://api.unsplash.com/search/photos', {
headers: {
Authorization: 'Client-ID s6X6PEJrJ2RuT00Rw3HkxZotqVFvEFnQlTvQ87vnog4'
},
params: {
query: 'cars'
}
})
console.log(response);
return response;
}
export default searchImages;
```
1. 但可以注意到這邊的query永遠是cars,但我們希望可以讓使用者輸入任意的關鍵字
因此首先我們給予一個變數叫做"term"作為參數傳入`searchImages`的function中。
2. 可以看到目前`creturn`的內容為"response",不過這個response代表的是我們抓到的「所有」資料,但我們希望只抓到我們所需的資料即可
下圖為變數response物件,我們只想要抓它的results的部份,可以看見他接在response底下的data底下,因此利用物件取值方式,也就是`response.data.results`。

```javascript!
import axios from "axios";
const searchImages = async (term) => {
const response = await axios.get('https://api.unsplash.com/search/photos', {
headers: {
Authorization: 'Client-ID s6X6PEJrJ2RuT00Rw3HkxZotqVFvEFnQlTvQ87vnog4'
},
params: {
query: term
}
})
return response.data.result;
}
export default searchImages;
```
****
## 5-67. Thinking About Data Flow
我們在這章節要思考,資料是如何在React App中流動的。以下我們可以先思考這個App可以做哪些事情?會做哪些事?
1. SearchBar這個元件中,包含一個文字輸入框,可以讓使用者輸入任意文字
2. 當使用者在文字輸入框按下"enter"後,代表我們要開始搜尋使用者輸入的關鍵字
3. 從[5-64](h[ttps://](https://hackmd.io/lE6S6Q0jRQWFBHbSc17SGg#5-64-Making-an-HTTP-Request))的後方有提到我們可以抓到一個物件,而使用者搜尋的內容將被存放進該物件中的一個陣列中。
4. 而該存放著圖片的陣列,必須傳至我們的ImageList元件檔案中
大致知道整個取得資料、回傳資料的流程後,我們再來要思考:要在什麼地方去呼叫`searchImage()`這個函式呢?(複習一下,該函式在`api.js`中,所以我們要思考該把這個函式import到哪裡)
**錯誤想法**
我們第一個想法可能會是這樣,想著我們要傳一個"term"變數進入`searchImage()`這個函式,而"term"變數會在`SearchBar.js`中,當使用者輸入並按下enter之後取得,再執行`searchImage()`這個函式,並把結果回傳給`SearchBar.js`的兄弟元件`ImageList.js`(如下圖):

:::success
(兄弟元件為sibiling component,同一層級的元件)
不過,在React中,如果兩元件為兄弟元件,他們之間是不知道對方的存在的(聽起來怎麼有點可悲)。
所以說是沒有任何方法可以讓這兩個元件之間做連接,也就是兄弟元件之間無法傳遞任何的資料。
:::
**正確想法**
所以我們必須找到這對兄弟元件的父母,這樣才能把資料分享給他們兩個,讓他們稍微變得有關連(?)
而我們可以從圖中輕易找到這對兄弟元件的父母就是`App.js`啊!!!所以我們將`searchImages()` import到`App.js`檔案中,接著就可以將"term"變數從`SearchBar.js`取得給`App.js`,並且將`searchImages()` 所return的"response.data.results"傳給`ImageList.js`檔案。(如下圖)
而至於如何實作?這就要依靠先前學過得"props system"了(props可以透過parent傳至children),所以我們將利用props來把images從`App.js`傳到`ImageList.js`。

****
## 5-68. Child to Parent Communication
先複習一下props system:

老師提到「props只能從parents傳入給child component」這句話不太準確。
因為我們現在就要來實作,將變數"term"透過props system從parents傳入給child component!

****
## 5-69. Implementing Child to Parent Communication
從[5-67](https://hackmd.io/lE6S6Q0jRQWFBHbSc17SGg#5-67-Thinking-About-Data-Flow)可以大概知道我們想要完成的目標。

不過我們先放大`App.js`與他的子元件`SearchBar.js`之間的關係:

<div style="border: black 1px solid; padding: 5px 5px; margin: 15px 0px">
<h3 style="margin: 0">App.js</h3>
</div>
首先,要在`App.js`中import SearchBar Component,並顯示至畫面上。並且設置一個function,命名為`handleSubmit()`,且我們會傳入參數"term"給`handleSubmit()`該function。
另外,要在SearchBar Component上加上事件等待使用者觸發,
亦即"onSubmit = {handleSubmit} ",這裡事件名稱為"onSubmit",當"onSubmit"事件被觸發,則會執行函式handleSubmit。
**App.js**
```javascript!
import SearchBar from './components/SearchBar'
function App(){
const handleClick = (term) => {
console.log("Do a search for "+term);
}
return(<div>
<SearchBar onSubmit = {handleClick}/>
</div>);
}
export default App;
```
BTW, onSubmit以及handleSubmit只是其中一種命名方法,是可以依照自己喜好去任意命名的。
<div style="border: black 1px solid; padding: 5px 5px; margin: 15px 0px">
<h3 style="margin: 0">SearchBar.js</h3>
</div>
接著,看到`SearchBar.js`的部份,回想一下前面剛在`App.js`中使用了SearchBar component,並且在該component中添加了屬性"onSubmit={handleClick}",新增屬性後,React會將我們添加的屬性放進一個props object中,再來就要把props作為「引數」傳入到`App.js`的子元件`SearchBar.js`中。
另外,這裡先將"car"值賦予給term變數,並從子元件(`App.js`)回傳給父元件(`SearchBar.js`)。
(這部份忘記可以參考[3-26](https://hackmd.io/yIqdj6sHS7OGPSN38Z_Miw#3-26-Introducing-the-Props-System))
**SearchBar.js**
```javascript!
function SearchBar({onSubmit}){
const handleClick = () => {
onSubmit("car");
}
return(<div>
<input />
<button onClick={handleClick}>Click</button>
</div>)
}
export default SearchBar;
```
這章節的實作方法有點打破以往的印象:「只能將資料從父元件傳給子元件」,這裡把term="car"從子元件帶回給父元件了!以下是這節的統整圖:

****
## 5-69. Implementing Child to Parent Communication
老師在上一章節運用button,使整個流程看起來更清楚,以及不管使用者在input輸入什麼內容,都只有回傳固定的"car",因此我們將在這個章節中做修改。
先回到最原始的狀態:
**SearchBar.js**

那首先針對HTML做修改,我們應該把`<input />` tag放在`<form></form>`中間,這樣當使用者在`<input />` 輸入內容,一些存在於瀏覽器自動的行為才會起作用(kick in)。
也就是你在`<input />` 輸入內容並按下enter鍵,你的瀏覽器就會自動觸發(trigger)`<form></form>`身上的提交事件(submit event)。
****
## 5-70. Handling Form Submission
這裡會再次談到關於form以及input的一些預設方法,但這些預設可能會對於這次專案有些問題,所以我們需要先從HTML去了解這兩者。
直接來看W3C的spec:[Forms](https://www.w3.org/TR/html401/interact/forms.html)
(PS input在form目錄之下)
其中應該跟[以下內容](https://www.w3.org/TR/html401/interact/forms.html#current-value)較相關(先不深入)
> The control's "current value" is first set to the initial value. Thereafter, the control's current value may be modified through user interaction and scripts.
>
> A control's initial value does not change. Thus, when a form is reset, each control's current value is reset to its initial value. If a control does not have an initial value, the effect of a form reset on that control is undefined.
在預設下,我們在`<input />` 輸入內容並按下enter鍵,你的瀏覽器就會自動觸發(trigger)`<form></form>`身上的提交事件(submit event),之後原先在`<input />` 輸入的內容,就會立即被清空(可以想像就是被提交出去的感覺)
不過我們希望是作一個搜尋引擎,想像一下假如我們在google搜尋資料,每次打上關鍵自按下enter後,搜尋框的內容被清掉是不是會很讓人感到困擾?
因此這裡我們可以利用一個語法來阻止預設的情形發生。
```javascript
event.preventDefault();
```
那麼就可以來實作了!
**App.js**
```javascript!
import SearchBar from './components/SearchBar'
function App(){
const handleClick = (term) => {
console.log("Do a search for "+term);
}
return(<div>
<SearchBar onSubmit = {handleClick}/>
</div>);
}
export default App;
```
**SearchBar.js**
```javascript!
function SearchBar({onSubmit}){
const handleFormSubmit = (event) => {
event.preventDefault();
onSubmit("car");
}
return(
<form onSubmit={handleFormSubmit}>
<input />
</form>)
}
export default SearchBar;
```
不過會發現目前還是沒有抓取使用者在input欄位輸入的內容,下章節將會進行說明。
****
## 5-71. Handling Input Elements
接著這章節將會說明如何抓取input裡,使用者所輸入的資料。
一開始我們可能會想,不就是先抓取input 這個DOM元素,然後再取他的value,這樣不就可以抓到input裡面的內容了嗎?(想法如下)
**SearchBar**
```javascript!
function SearchBar({onSubmit}){
const handleFormSubmit = (event) => {
event.preventDefault();
onSubmit(document.querySelector('input').value);
}
return(
<form onSubmit={handleFormSubmit}>
<input />
</form>)
}
export default SearchBar;
```
當我們嘗試執行這段程式碼,會發現他是可以執行的,沒什麼問題,但是!!!!!
:::danger
老師提醒:絕對不要寫出這種方法!!!!!!

老師提到,在react中不要用類似query selector的方式嘗試抓取input的資料,老師還說寫出這種方法,在面試中保證會被拒絕掉。
那這是因為React在處理form的一些元件(例如說text input、checkboxes、radio button等等)有一點點的特別,他大致上有5個步驟,接下來會繼續說明。
:::

<div style="border: black 1px solid; padding: 5px 5px; margin: 15px 0px">
<h3 style="margin: 0">Step1. Create a new piece of state</h3>
</div>
先創建新的State,照前面所學運用useState來創建。

**SearchBar.js**
```javascript=
import {useState} from 'react';
function SearchBar({onSubmit}){
const [term, setTerm] = useState('');
const handleFormSubmit = (event) => {
event.preventDefault();
onSubmit("car");
}
return(
<form onSubmit={handleFormSubmit}>
<input />
</form>)
}
export default SearchBar;
```
<div style="border: black 1px solid; padding: 5px 5px; margin: 15px 0px">
<h3 style="margin: 0">Step2. Create an event handler to watch for the 'onChange' event.</h3>
</div>
接著,要利用'onChange'來觀察使用者在input中做了哪些事情?(例如說複製、貼上、輸入、刪除等等)
所以我們新增onChange事件到input上,當onChange被觸發,那麼就會執行handleChange。

```javascript!
import {useState} from 'react';
function SearchBar({onSubmit}){
const [term, setTerm] = useState('');
const handleFormSubmit = (event) => {
event.preventDefault();
onSubmit("car");
}
const handleChange = () =>{
}
return(
<form onSubmit={handleFormSubmit}>
<input onChange={handleChange}/>
</form>)
}
export default SearchBar;
```
<div style="border: black 1px solid; padding: 5px 5px; margin: 15px 0px">
<h3 style="margin: 0">Step3. When the 'onChange' event fires, get the value from the input.</h3>
</div>
首先,我們只要在input裡面有任何的動作,就等於觸發onChange這個event,所以我們先試著在觸發時,讓瀏覽器印出"event"物件(如下圖)
**觸發onChange時會執行的函式(印出"event")**

**event物件們**

而其中我們最關心event物件中的value,我們只要稍微找一下,就可以發現value出現在event物件底下的taget之中:(下圖中最後一行)

所以這裡我們先改為印出event.target.value看看。

此時就可以看見我在input中逐字輸入字串的內容

<div style="border: black 1px solid; padding: 5px 5px; margin: 15px 0px">
<h3 style="margin: 0">Step4. Take that value from the input and use it to update your state.</h3>
</div>
再來我們除了要拿到input的內容之外,還要去更新state。而其中去改變、更新state的,就是setState,亦即此處的setTerm。作法很簡單,先把console.log()改成setTerm()即可。
意思就是每當onChange event被觸發,就會執行handleChange,而該函式會做的事情就是setTerm(event.target.value),用文字說明即每次更新就會把"event.target.value"重新渲染給term。

<div style="border: black 1px solid; padding: 5px 5px; margin: 15px 0px">
<h3 style="margin: 0">Step5. Pass your state to the input as the value prop.</h3>
</div>
接著,把term(term即每次使用者在input有任何動作,就會被重新渲染成使用者在input的輸入內容)作為input中的value prop。

****
## 5-72. [Optional] OK But Why?
延續上一章節,這章節將解釋上一張節的步驟為什麼要這麼做呢?
首先,第一個要談的是`<input />` 裡的value prop,先以HTML角度來看,`<input />` 擁有value屬性,以我們下方程式碼的例子來看,沒有設置type屬性,因此會視為預設type="text"。
```htmlembedded!
<input value="hi there!" />
```

而我們可以從W3C中(下圖)得知,type="text"時,value屬性的值,即為初始狀態時input的輸入欄位中會顯示的內容。

因此,當我們在上一章節,寫出如下的內容,代表的就是使用者在input欄位輸入什麼,都會顯示在欄位內
```htmlembedded!
<input value={term} />
```
老師提供上章節整個運作的流程圖:

所以為什麼在React中,抓取input裡面的值要怎麼麻煩呢????

****
## 5-75. Reminder on Async:Await
看到App.js的部份,可以發現若我們只是一般的使用api.js中的`searchImage()`,那他印出的結果會是下方第二張圖片。
**同步:App.js**

**同步:印出結果**
會得到一個promise物件,裡面寫著pending,代表執行`searchImage()`後他還在處理,他需要一點時間,但前面提過JS是同步語言,所以JS不給任何時間,會立即執行下一行。

因此,我們這裡要加上async、await讓這段程式碼變成非同步處理。
**非同步:App.js**

**非同步:印出結果**

****
## 5-76. Communicating the List of Images Down
在上一個章節中,我們可以很清楚「result變數」代表著使用者所輸入的內容,搜尋到的圖片。
這裡我們想將「result變數」,顯示在「ImageList」這個component上。
也就是說,我們希望每次輸入者輸入內容後,「ImageList」這個component上的圖片可以不斷的更新(update)。而在React中提到update,百分之百就是要使用到==state system==!!!!
**App.js**
```javascript
import { useState } from 'react';
import SearchBar from './components/SearchBar';
import ImageList from './components/ImageList';
import searchImages from './api';
function App(){
const [images, setImages] = useState([]);
const handleClick = async (term) => {
const result = await searchImages(term);
setImages(result);
}
return(<div>
<SearchBar onSubmit = {handleClick}/>
<ImageList images={images}/>
</div>);
}
export default App;
```
**ImageList.js**
```javascript
function ImageList({images}){
return<div>ImageList:{images.length}</div>
}
export default ImageList;
```
很酷的是,可以發現`images`在App.js中視作為state使用,而在ImageList中則作為props使用,這意味著一個變數可以同時作為state以及props。
**作為state使用**

**作為props使用**

****
## 5-77. Building a List of Images
我們可以從下圖看到,我們從API取得的圖片,有相當多的資訊,不過我們主要來看"id",因為id是唯一的,可以用來代表一張圖片。除此之外,我們還要利用前面章節學過得`map()`幫我們將圖片轉換成component的一部分。

底下的流程圖,便是我們想要達成的:

**ImageList**
利用map()
```javascript!
import ImageShow from './ImageShow';
function ImageList({images}){
const renderedImages = images.map((image) => {
return <ImageShow image = {image}/>
})
return<div>{renderedImages}</div>
}
export default ImageList;
```
**ImageShow**
```javascript!
function ImageShow({image}){
return<div>{image.id}</div>
}
export default ImageShow;
```
**呈現畫面**
接著就可以看到螢幕上,顯示使用者輸入內容,透過API抓到的十筆資料的id了。

不過來到這裡會遇到一個error:

他要我們加上一個"key" prop!而這將會在下一節提到。
****
## 5-78. Handling List Updates
在開始之前,我們先把id給替換掉,換成alt_description,這樣對我們來說辨別度比較大,比較好操作。

改完之後,應該可以看到頁面顯示改為下圖:

以下是我們想做的事情,將圖片物件中的alt_description提出,放到ImageShow元件中,並且這麼描述最後要放入div裡面。

接著,要記得一件事情,每當React中的state更新了,他就會rerender要變動的元件,以及底下的子元件。
那麼如何更新呢?老師給了plan A跟plan B
**Plan A**
移除所有存在的HTML,再整個全部重新建立
這種方式會成功,但是太耗能了,所以我們不應該使用這個辦法

**Plan B**
那麼就是要利用Plan B了!在step 0時,我們先加上key props,並且賦予這個key的值為image.id。
再來,進入第一步驟,重新render後,react會去比較先前與目前的key,知道有哪些變化。

第二步驟,react會以最少的步驟,去改變已存在的DOM元素。

****
## 5-79. Notes on Keys

首先,對ImageShow元件加上key props,此時再回到瀏覽器會發現不會在顯示error了(5-77曾提過得error)。

第二點中,提到'Add the key to the top-most JSX element in the list',表示著記得把key移至整個JSX中的最外層(例如最外層是div,那請把key放在div這一層之上)。
另外可能會有些疑問,我們在第四節的動物專案中並沒有id,那當時的key的值是什麼呢?答案是'index',這就是當初都沒說的index的用處,index就是賦予給key props 的值。

****
## 5-80. Displaying Images
再來,要把圖片顯示在螢幕之上了。我們可以從API取得的資訊中看到"urls",而底下有幾種不同格式,而為了減輕整個app的花費,我們這邊會選擇"small"的連結。

