Edamam APIを取得し、検索フォームから該当する画像とレシピを取得・表示

### 完成図
![20221211_Edamam API.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/829500/a7a2e88f-26b1-293d-14c3-ba5e3b682a20.png)




参考動画（ほぼ丸パクリです。動画自体とてもわかりやすく、動画投稿者に感謝です。）
https://www.youtube.com/watch?v=XNOWTI2sqQ8

Edamam URL (ここから取得できます。)
https://www.edamam.com/



### 実装結果

App.jsx 
```
import React from 'react';
 
// components
import Edamam from './containers/api/Edamam'
 
function App() {
 return (
   <>
     <Edamam />
   </>
 );
}
 
export default App;
``` 


Edamam.jsx (EdamamAPIを取得するためのcomponent)
```
import React, { useState,useEffect } from 'react'
 
// components
import Recipe from '../Recipe'
 
const Edamam = () => {
 
 // keyの設置
 const APPLICATION_ID = 'ここはEdamamAPIから取得したIDを記述'
 const APPLICATION_KEY = 'ここはEdamamAPIから取得したKEYを記述'
 
 
 // stateの作成
 const [search, setSearch] = useState("")
 const [query, setQuery] = useState("banana")
 const [recipes, setRecipes] = useState([])
 
 // APIの取得
 const getRecipes = async() => {
   const response = await fetch(`https://api.edamam.com/search?q=${query}&app_id=${APPLICATION_ID}&app_key=${APPLICATION_KEY}`)
   const data = await response.json()
   setRecipes(data.hits)
   console.log(data.hits)
 }
 
 const getSearch = e => {
   e.preventDefault();
   setQuery(search);
   setSearch('');
 }
 
 // 入力した時にsearchの更新
 const updateSearch = (e) => {
   setSearch(e.target.value)
 }
 
 useEffect(() => {
   getRecipes();
   // eslint-disable-next-line react-hooks/exhaustive-deps
 }, [query] )
 
 return (
   <>
     <>Edamam</>
     <form onSubmit={getSearch}>
       <input type='text' value={search} onChange={updateSearch} />
       <button type='submit'>検索</button>
     </form>
     <div>
       {recipes.map(recipe => (
         <Recipe
           key={recipe.recipe.label}
           title={recipe.recipe.title}
           calories={recipe.recipe.calories}
           image={recipe.recipe.image}
           ingredients={recipe.recipe.ingredients}
         />
       ))}
     </div>
   </>
 )
}
 
export default Edamam;
``` 

Recipe.jsx（Edamam APIで取得した内容を表示させるためのcomponent）
```
import React from 'react'
 
const Recipe = (props) => {
 console.log(props)
  return (
    <div>
       <h1>{props.title}</h1>
       <ol>
         {props.ingredients.map(ingredients => (
           <li>
             {ingredients.text}
           </li>
         ))}
       </ol>
       <p>{props.calories} cal</p>
       <img src={props.image} alt='' />
    </div>
  )
}
 
export default Recipe
```

### 実装手順
実装手順は以下の通りです。

①検索フォームの作成

②APIの取得

③APIから取得してきたデータの表示


次にcomponentの部分ごとにどんなことを行っているのか解説していきます。
①の検索フォームの作成から解説していきます。
Edamam.jsxでは、検索フォームとAPIの取得をしています。
検索フォームは下記の通り。

```
<form onSubmit={getSearch}>
  <input type='text' value={search} onChange={updateSearch} />
  <button type='submit'>検索</button>
</form>
```

ここでは、inputタグで入力フォーム、buttonタグで検索ボタンを作成しました。
入力フォームに値を入力し、検索ボタンを押すことでAPIを取得・表示させるような仕様となっております。

formタグで囲み、onSubmitのイベントハンドラーの中にgetSearchという関数が書かれているので、入力フォームに値を入れ、ボタンを押すことで、getSearchという関数が実行されます。
また、inputタグには、valueとonChangeのイベントハンドラーを記述しています。
valueでは、入力された値がsearchという変数に格納されます。
onChangeは入力フォームに値が入力されるたびにupdateSearchという関数が実行されるような状態となっております。

では、searchという変数、getSearch、updateSearchという関数は何を表しているのか。

Edamam.jsx（一部抜粋）
```
// stateの作成
const [search, setSearch] = useState("")
const [query, setQuery] = useState("banana")
 
// stateの更新
const getSearch = e => {
   e.preventDefault();
   setQuery(search);
   setSearch('');
 }
 
// stateの更新
const updateSearch = (e) => {
   setSearch(e.target.value)
 }
```

上記より、searchはstateを表し、入力フォームに値を入力したとき、updateSearch関数が実行されるので、setSearch(e.target.value)　より、searchの値が更新されます。
つまり、stateのsearchは、入力フォームの値を管理するためのstateとなります。

また、検索ボタンを押したとき、getSearch関数が実行されます。
中身を見ると、e.preventDefault()が実行されています。

preventDeafaultはイベントが発生したときのデフォルト動作をキャンセルすることができます。
（参考記事：https://www.javadrive.jp/javascript/event/index12.html ）

今回の場合、updateSearch関数は、onChangeというイベントが発生した時に実行されているので、onChangeのデフォルトの動きをキャンセルしています。
（デフォルトは、どんな動きなのか？？？）

さらに、setQuery(search) でqueryというstateの中身をsearchという入力した値に更新し、
setSearch('') でstateのsearchの中身を空にしています。
こうすることで、入力した値が入力フォーム内から消えるようになり、検索ボタンを押した後は入力フォーム内の値がリセットされるようになります。



次に②APIの取得方法について解説していきます。
Edamam  APIを取得している箇所は下記の部分です。

Edamam.jsx
```
 const getRecipes = async() => {
   const response = await fetch(`https://api.edamam.com/search?q=${query}&app_id=${APPLICATION_ID}&app_key=${APPLICATION_KEY}`)
   const data = await response.json()
   setRecipes(data.hits)
   console.log(data.hits)
 }
```
上記では、getRecipesという関数が実行されることで、Edamam APIを叩き、取得しています。また、recipesというstateの中に入れています。



最後に③APIから取得してきたデータの表示を解説していきます。
表示部分は下記の通り。

Edamam.jsx
```
<div>
  {recipes.map(recipe => (
     <Recipe
       key={recipe.recipe.label}
       title={recipe.recipe.title}
       calories={recipe.recipe.calories}
       image={recipe.recipe.image}
       ingredients={recipe.recipe.ingredients}
     />
  ))}
</div>
```
Recipe.jsx
```
import React from 'react'
 
const Recipe = (props) => {
 console.log(props)
  return (
    <div>
       <h1>{props.title}</h1>
       <ol>
         {props.ingredients.map(ingredients => (
           <li>
             {ingredients.text}
           </li>
         ))}
       </ol>
       <p>{props.calories} cal</p>
       <img src={props.image} alt='' />
    </div>
  )
}
 
export default Recipe
```

Edamam component で recipes へ代入したEdamam APIのデータを map で繰り返し処理を実行し、recipes から取り出した各プロパティを Recipe component へ props として渡しています。

そしてRecipe component ではpropsを受け取り、そのpropsに含まれる値を抽出することで、Edamam APIのデータを表示させています。

### まとめ
今回は、外部APIのEdamam APIを取得・表示する方法を紹介させていただきました。
ただ実務だと、外部APIではなく、railsで管理しているデータをrails API(rails controller)を叩くことで、データを取得・表示することが多いです。
次回以降はrailsも使用し、データを取得・表示することができるようにしていきたいです。
最後まで読んでいただきありがとうございました。