Reactでアプリを作成をしました【2】【天気アプリ②】

##はじめに

####学習するに至った経緯

2020年より、未経験からエンジニアへの転職を目指し、某プログラミングスクールへ通う。
入学後、『Ruby』を未経験から学ぶ人が多いのと『Ruby』の求人が思っていた以上に少ないので、
卒業後、フロントエンドのエンジニアを目指す事に。
Reactの学習した事を言語化し、認識の深化による備忘録として記載。

##具体的な手順

1. PlayGroundを探索し、パラメータを設定する。
2. ChakraUIでフロントエンドを設定する。
3. リクエストを処理する。
4. レスポンスを使ってフロントエンドに表示する。
5. ChakraUIを使ってローディング状態を表示し、ダークモードを有効にする。

##GraphQLとは
『GraphQL』とは、Facebook社が開発・提供しているWeb APIのクエリ言語。
クエリ言語とスキーマ言語で構成されたWeb APIの規格で、クエリ言語という言葉から想像できるように、サーバーにデータを問い合わせることができる。GraphQLは、Facebook社によって2012年に開発がスタートし、2015年にオープンソース化された。
GraphQLは様々なOSに対応しており、クロスプラットフォーム開発が可能。また、JavaやJavaScript、Rubyなど多くのプログラミング言語に対応している点も特徴といえる。

##GraphQLのメリット・デメリットとは
####GraphQLのメリットは、下記の3つ。

① 少ないリクエストで複数のリソースにアクセス可能
② 最小限のデータのみを取得
③ 既存のクエリを残しつつAPIの更新が可能

###① 少ないリクエストで複数のリソースにアクセス可能
HTTP構造に依存しないため、複数のページに必要なデータを取得ができる。これにより、リクエストが無駄になることはないため、パフォーマンスの低下を招きづらく、またサーバー側に負担をかけないため、ハードウェアにかかるコストも抑えられる。

###② 最小限のデータのみを取得
GraphQLは、取得するデータを指定してリクエストすることができる。例えば、名前やメールアドレスだけを指定してサーバーに問い合わせることが可能で、これにより、必要なデータのみを取得することができるため、通信の無駄を防ぐことができ、パフォーマンスの向上にも期待ができる。

###③ 既存のクエリを破壊せずにAPIの更新が可能
フィールドの削除を自ら行ったりしない限り、クエリが損なわれる心配がない。これにより、クライアントサイドの振る舞いに影響を与えないので、更新後も安定的なリクエストを維持できる。

####GraphQLのデメリットは下記の2つ。

① 新しい技術なので情報量が少ない
② キャッシュが複雑になる

###① 新しい技術なので情報量が少ない
GraphQLは、2015年に登場した比較的新しい規格のため、情報量が少ない。独学の場合、少ない情報の中で勉強をしなければならないので、マスターするまでに一定の時間がかかる。

###② キャッシュが複雑になる
GraphQLのキャッシュはURLを生成せず、レスポンス内容も異なるため、複雑である。そのため、CDNによるオフロードの実現が難しかったり、クライアントサイドへ悪影響を及ぼしたりする可能性がある。


##1. PlayGroundを探索し、パラメータを設定する
Weather APIにアクセスする。
インタラクティブなプレイグラウンドが動いているのがわかる。ここに表示されているのは、たくさんのパラメータを含んだクエリです。このクエリを実行すると、入力されたパラメータごとに結果データを含むレスポンスが表示される。ここで使用するパラメータは、name, weather, temperature, と wind.

修正後のクエリは以下のようになる。

```
query {
  getCityByName(name: <Your_City>) {
    name
    weather {
      summary {
        title
        description
      }
      temperature {
        actual
        feelsLike
      }
      wind {
        speed
      }
    }
  }
}
```

Your_Cityの代わりに、自分の住んでいる都市または任意の都市を入力する。
まだ、すべてのデータを使うわけではありませんが、とりあえず、クエリを実行する。

##2. ChakraUIによるフロントエンドの設定
Chakra UIを使ってフロントエンドを設定する。

npmを使ってChakra UIをインストールする。

```
npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4
```
ルートファイル（src/index.js）にプロバイダを設定する。

```index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { ChakraProvider, ColorModeScript } from '@chakra-ui/react'
import App from './components/App'

ReactDOM.render(
  <ChakraProvider>
    <ColorModeScript initialColorMode="light" />
    <App />
  </ChakraProvider>,
  document.getElementById('root')
)
```
ColorModeScript はライトモードとダークモードをトラッキングするためのコンポーネントで、これは最後の方で有効にする。
src/componentsディレクトリにDashboard.jsという新しいコンポーネントを作成する。

ダークモードを切り替えるためのアイコンボタンを作成。

```Dashboard.js
import React from 'react'
import { VStack, IconButton } from '@chakra-ui/react'

export default function Dashboard ( ) {
return (
  <VSack>
    <IconButton
        aria-label="Toggle Dark Mode"
        m="8"
        size="md"
        alignSelf="flex-end"
      />
  </VStack>
)
}
```
見出しと入力欄を準備する

```
import React, { useState } from 'react'
import {
  Input,
  VStack,
  HStack,
  IconButton,
  Heading
} from '@chakra-ui/react'
import { FiSearch } from 'react-icons/fi'

export default function Dashboard ( ) {

  const [result, setResult] = useState('')
  const [value, setValue] = useState('')

 return (
<VStack>
      <IconButton
        aria-label="Toggle Dark Mode"
        m="8"
        size="md"
        alignSelf="flex-end"
      />
      <Heading mb="8" size="xl">
        Search your city
      </Heading>
      <HStack>
        <Input
          type="text"
          placeholder="Enter city name"
          value={result}
          onChange={(event) => setResult(event.target.value)}
        />
        <IconButton
          aria-label="Search city button"
          icon={<FiSearch />}
          onClick={() => {
            setValue(result)
            getWeather()
            //Errormessage()
          }}
        >
          Search
        </IconButton>
      </HStack>
</VStack>
)
}
```
これで入力フィールドの作成は完了。入力フィールドからのデータをコンソールログに記録して、ステートが本来の動作をしているかどうかを確認。
※ 何かエラーが発生した場合は、下記の GitHub リポジトリを確認すること。

##3. リクエストの処理
APIを操作するためには、アプリにGraphQLを設定するためのパッケージをインストールする。
以下のパッケージをインストールする。

```
npm install graphql @apollo/client
```

ChakraProvider を App コンポーネントに入力したように、GraphQL リクエストを処理する App にも入力する。
src/components ディレクトリ内の App.js ファイルを以下のように変更する。

```App.js
import React from 'react'
import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client'
import Dashboard from './Dashboard'

export default function App() {
  const client = new ApolloClient({
    cache: new InMemoryCache(),
    uri: 'https://graphql-weather-api.herokuapp.com/',
  })

  return (
    <ApolloProvider client={client}>
      <Dashboard />
    </ApolloProvider>
  )
}
```
ここでは定数を定義し、2つのパラメータ（cacheとuri）を入力してApolloクライアントを初期化する。
※ uri パラメータはここで使用しているデフォルトの API エンドポイントで、cache はアプリが同じデータに対して不必要なリクエストを送らないようにするためのもの。

次に、Queries.jsという新しいファイルを作成する。このファイルは、src/components/graphqlという別のディレクトリに作成する。このファイルには、先ほどプレイグラウンドで作成したクエリが格納される。

```Queries.js
import { gql } from '@apollo/client'

export const GET_WEATHER_QUERY = gql`
  query getCityByName($name: String!) {
    getCityByName(name: $name) {
      name
      country
      weather {
        summary {
          title
          description
        }
        temperature {
          actual
          feelsLike
        }
        wind {
          speed
        }
      }
    }
  }
```

都市の名前を渡す場合は、入力フィールドから渡された $name に渡す。


　※ GraphQLのスキーマタイプについて詳しく知りたい方は、以下のドキュメントを確認する。

https://graphql.org/learn/schema/


APIエンドポイントにリクエストを行い、そのレスポンスをログに記録する。
Dashboard.jsファイルを開き、以下のように変更する。

```Dashboard.js
import { useLazyQuery } from '@apollo/client'
import { GET_WEATHER_QUERY } from '../graphql/Queries'

export default function Dashboard ( ) {
  const [getWeather, { loading, data, error }] = useLazyQuery(
    GET_WEATHER_QUERY,
    {
      variables: { name: value },
    }
  )

// Search button for input field
<IconButton
    aria-label="Search city button"
    icon={<FiSearch />}
    onClick={() => {
      setValue(result)
      getWeather()
      }}
 >
    Search
</IconButton>
}
```
ボタンがクリックされると、2つのイベントが発生し、1つは、先ほど定義したsetValue()ステートとgetWeather()関数で、これによりリクエストが送信され、レスポンスが取得されてdata変数に格納される。

値の状態は、先ほどsrc/components/graphql/Queries.jsファイルで定義したnameプロパティに渡される。
これで、console.log(data)を実行すると、APIエンドポイントから返信されたレスポンスを見ることができるはず。

##4. レスポンスを使ってフロントエンドに表示する
エンドポイントから返ってくるレスポンスは、先ほどdata変数で取得したオブジェクトで、ここでは、レスポンスを洗練されたテーブルの形でフロントエンドに表示する。ここでは、Chakra UiのTableコンポーネントを利用する。

※ 迷ったときはChakra UIのドキュメントを参照してください。

https://chakra-ui.com/docs/data-display/table

以下のコンポーネントをインポートする。

```
import {
  Stack,
  Table,
  Tbody,
  Tr,
  Td,
  TableCaption,
  StackDivider,
  HStack,
  Divider,
} from '@chakra-ui/react'

// Dashboard component
<Stack>
        <Divider mt="4" mb="4" borderColor="gray.100" />
        {data && (
          <>
            <VStack
              divider={<StackDivider />}
              borderColor="gray.100"
              borderWidth="2px"
              p="4"
              borderRadius="lg"
              w="100%"
              maxW={{ base: '90vw', sm: '80vw', lg: '50vw', xl: '40vw' }}
              alignItems="stretch"
            >
              <Table variant="simple">
                <TableCaption>
                  Weather in {data.getCityByName.name} right now
                </TableCaption>

                <Tbody>
                  <Tr>
                    <Td>Temperature</Td>

                    <Td textAlign="right">
                      {Math.floor(
                        data.getCityByName.weather.temperature.actual - 273
                      ) + ' '}
                      Deg C
                    </Td>
                  </Tr>
                  <Tr>
                    <Td>Feels like</Td>
                    <Td textAlign="right">
                      {data.getCityByName.weather.summary.description}
                    </Td>
                  </Tr>
                  <Tr>
                    <Td>Wind speed (mph)</Td>

                    <Td textAlign="right">
                      {data.getCityByName.weather.wind.speed + ' '}
                    </Td>
                  </Tr>
                </Tbody>
              </Table>
            </VStack>
          </>
        )}
      </Stack>
```

最も重要なことは、レスポンスから送られてくるデータを手に入れることができるということで、デフォルトでレスポンスから返されたケルビン単位を、公式 - 温度（ケルビン） - 273 = 温度（摂氏） を使用して摂氏に変換している。

##5. ChakraUIによるローディング状態の表示とダークモードの有効化
GraphQLサーバーからリクエストを取得するロジックを書いているときに、loadingという変数をデストラクションした状態を利用して、リクエストを取得する処理が進行中かどうかを判断できる。ここではChakra UIのスピナーコンポーネントを利用できる。

スピナーを表示するためのロジックは以下のようになる。

```
import { spinner } from "@chakra-ui/react" 
// Dashboard.js component
<Stack>
        {loading && (
          <>
            <Spinner
              thickness="4px"
              speed="0.65s"
              emptyColor="gray.200"
              color="blue.500"
              size="xl"
            />
          </>
        )}
</Stack>
```
算子を使うことで、読み込み状態がtrueの場合にのみスピナーコンポーネントが表示される。

それでは、アプリのダークモードを有効にする。

Dashboard.jsファイルに移動して、ダークモードのトグルボタンを機能させる。

```Dashboard.js
import { useColorMode } from "@chakra-ui/react
// for the icon to change on toggle - 
// if light: show Moon Icon else show Sun Icon
import { FiMoon, FiSun } from 'react-icons/fi'

const { colorMode, toggleColorMode } = useColorMode()

// toggle button for dark/light mode
<IconButton
        aria-label="Toggle Dark Mode"
        icon={colorMode === 'light' ? <FiMoon /> : <FiSun />}
        m="8"
        size="md"
        alignSelf="flex-end"
        onClick={toggleColorMode}
/>
```
これで，ユーザがボタンをクリックすると，toggleColorMode が起動するようになり、これにより、colorModeの状態が変化し、三項演算子で実装された明暗に基づいてアイコンが変化する。これで、自由に使えるWeatherアプリケーションの作成が完了。





##Resources to build the project


https://github.com/GeoBrodas/graphql-weatherapp



https://graphqlweather.netlify.app/

##参考サイト
[Build a Weather Application using GraphQL and Chakra UI]
(https://dev.to/geobrodas/build-a-weather-application-using-graphql-and-chakra-ui-4j6o)
[GraphQLとは？RESTとの違いや導入事例を紹介]
(https://udemy.benesse.co.jp/development/system/graphql.html)