React hooks+axiosを使ったAPIからデータ取得方法

#はじめに

React hooksでHotPepper APIを叩いてデータを取得し画面表示～画面遷移を実装したので
React hooksとaxiosを使ったAPIデータの取得方法や`GlobalState`を使ったデータ保持方法について紹介します。

# 対象

- React初学者
- React hooksについて知りたい方



# React hooks

まずは、React hooksについて簡単に紹介します。
React hooksは`React 16.8`から追加された機能で[公式サイト](https://ja.reactjs.org/docs/hooks-intro.html)では、以下のように説明があります。
> フック (hook) は React 16.8 で追加された新機能です。state などの React の機能を、クラスを書かずに使えるようになります。

`React hooks`の登場により、クラスコンポーネントでしか使えなかった機能が、関数コンポーネントでも使えるようになりました。


## クラスコンポーネントと関数コンポーネント

そもそもなぜ関数コンポーネントの方がいいのかというところから説明します。簡単な例として、[公式ドキュメント](https://ja.reactjs.org/docs/hooks-overview.html)から「ボタンをクリックすると、カウンターの値が増える」ソースコードを関数コンポーネントとクラスコンポーネントで作成した例を示します。


```react:クラスコンポーネントでの実装
import React from 'react';

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
  }
  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}


```

```react:関数コンポーネントでの実装
import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me </button>
    </div>
   
  );
}

```



この二つのコードを比べるとわかるように、関数コンポーネントを使用することで以下のメリットが挙げられます。

- コード量の削減
- 可読性の向上
- `this`を使用せず実装可能


さらに、従来の関数コンポーネントでは`state`を持つことができなかったのですが、`React hooks`の登場によりそれが可能となりました。
次に、今回はWebアプリ開発でよく使われる`useEffect`、`useContext`、`useReducer`を使い実装したのでそれぞれについて紹介します。

## useEffect

```javascript
import { useEffect } from 'react';
useEffect(() => {
    // 実行処理
}, []));

```

useEffectは、クラスコンポーネントである`componentDidMount`、`componentDidUpdate`、`componentWillUnmount` のメソッドの代わりに使用可能です。
useEffectを使うと、関数の実行をレンダリング後まで遅らせることができ、API通信やDOMの書き換えを関の処理などを関数コンポーネントで実装できます。
今回は`useEffect`内にホットペッパーからAPIデータを取得する処理を実装しました。


## useContext

親から子コンポーネント間でのデータ受け渡しを階層に関係なくデータを渡せる機能です。

![context_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2004450/0ba0d10f-a4eb-4fb0-9d0d-7ce46f54d99c.png)



上記の図のように従来は`props`によるバケツリレーをしていたが、
`useContext`を使うことでコンポーネントを超えて値を引き渡すことが可能です。




## useReducer

現在の`state`と`action`を受け取って、`state`を更新し、新しい`state`を返します。
`useState`も同じことができますが、複雑な状態遷移を処理する場合は`useReducer`を使います。
`useEffect`と`useState`の違いについては以下の記事を参考にしてください。

https://qiita.com/seira/items/2fbad56e84bda885c84c




#  GlobalStateでの管理

GlobalStateを作ることで、様々なコンポーネントから`state`へのアクセスが可能となります。
コンポーネント単位でpropsを使用せずデータを管理しようとすると、Reduxが必要でしたが
`useContext`、`useReducer`を使うことでデータ管理が可能となりました。


```javascript
import React,{createContext,useReducer} from 'react'

// initialStateの初期化
const initialState ={
    searchData: [],
    selected: {}
}

// reducerによるデータ管理
const reducer = (state,action) => {

    switch(action.type) {
        case 'SET_DATA':
            return {...state, searchData: action.payload.searchData}
        case 'SET_SELECTED':
            return {...state,selected: action.payload.selected}
        default:
            return state
    }
}

// globalStateを各コンポーネントで使用できるようにする。
export const Store = createContext({
    globalState: initialState,
    setGlobalState: () => null
})

//コンポーネントとして登録し、Providerでラップしデータを渡す。
//childernを受けているので、<Store.Provider>で全体を包むことで親要素を全て取得することができる。
export const StoreProvider = ({children}) => {
    const [globalState,setGlobalState] = useReducer(reducer,initialState)

    return (
        <Store.Provider value={{globalState,setGlobalState}}>{children}</Store.Provider>
    )
}

export default StoreProvider
```

`GlobalState`を参照する際は`useContext`を使います。



```react
//StoreからGlobalStateを取り出す
 const { globalState,setGlobalState} = useContext(Store)
```





# axiosを使ったAPI取得

`axios`とはブラウザやnode.js上で動くPromiseベースのHTTPクライアントです。

https://github.com/axios/axios


## axiosの導入

次のコマンドを実行してインストールします。

```sh
$ npm install axios --save
```

## HotPepper API

HotPepper APIは株式会社リクルートが提供するAPIで[ホットペッパーグルメ](https://www.hotpepper.jp/)に使われています。APIの詳細については以下URLを参考にしてください。

https://webservice.recruit.co.jp/doc/hotpepper/reference.html


## HotPepper API取得部分



```javascript
import axios from 'axios'

const KEY = 'HotpepperのAPIキー'

// axiosのインスタンス作成
const hotpepper = axios.create ({
    baseURL: 'http://webservice.recruit.co.jp/hotpepper/gourmet/v1'
})

export const fetchSearchData = async() =>{
    //大エリアコード=Z011(東京)のお店を検索
        return await hotpepper.get('',{
            params: {
                key:KEY,
                large_area:'Z011',
                format: 'json'  
             }
         })
    }
}
```

## useEffect内でAPIを実行


useEffect内でAPI取得部分を実装したソースコードを以下に示します。

```javascript
    useEffect(() =>{
        fetchSearchData().then((res) => {
            console.log(res)
        })
    },[])
```

APIを叩いたブラウザ画面にアクセスし、開発者ツールのコンソールを確認します。
`res`にHotpepper APIから取得したデータが入っていることがわかります。

![hotpepperdata.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2004450/68c9428b-5896-b1cc-86fc-4dbbae370729.png)

# GlobalstateにAPIデータが格納されているか確認

Facebook社から提供されている公式のReact用のデバッグツールである[React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=ja)を利用し確認します。
`Context.Provider`コンポーネントの`Globalstate`にAPIデータが格納されていることがわかります。


![スクリーンショット 2021-12-10 102829.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2004450/3cc6489c-be6d-8eac-b588-60d6b5ff70ce.png)








# 最後に

今回は、初学者向けに`React hooks`とは何か？から`axios`を使ったAPIデータの取得方法やAPIデータを`GlobalState`で管理する方法について紹介しました。
Reactについて調べていると、`Redux`や`React hooks`など様々な用語や記事が出てくると思います。
下記記事にもあるように徐々に`Redux`から`React hooks`が主流になるのではないかなと思います。
皆様もぜひReact hooksを活用してみてくださいね。


https://tech-parrot.com/react/react-hooks-redux/