React SpectrumでBuild a movie search app using React hooksを作ってみた

# React Spectrumがすごい
React Spectrumがすごそうということで、実際に使って開発してみました。React Spectrum自体の設計のクオリティの高さは、[Adobe製デザインシステム「React Spectrum」がすごいので紹介したい](https://qiita.com/so99ynoodles/items/bc924b7ee8c265b09723)の記事が詳しいです。内部の作りはさておき、開発者視点で良かったと感じた点は以下です。

- **Adobeが開発**しているので信頼度が高い
- Componentの**プロパティの統一感**があり、わかりやすく使いやすい
- **Flex/Gridが簡単にかける**
  - 基本的にタグで書いていく思想？でCSSを書かなくても大体いける。Flex/Gridもタグがある。
  - classNameのプロパティは「UNSAFE_className」になっており、説明にも「最後の手段」と。

# Build a movie search app using React hooksについて
[2020年のフロントエンドマスターになりたければこの9プロジェクトを作れ](https://qiita.com/rana_kualu/items/915345b8f3f870cfe2aa)で１つめに紹介されていたプロジェクトです。元のサイトは[こちら](https://www.freecodecamp.org/news/how-to-build-a-movie-search-app-using-react-hooks-24eb72ddfaf7/)。フロントエンドマスターを目指した方なら作ってみたことがあるはず！React Spectrumを試すちょうどよい題材として活用させてもらいました。

# React Spectrumを使ってカスタマイズ
## 完成したイメージ
こんな感じです。
（デザインがダサいと感じたら、それはReact Spectrumではなく私のセンスの問題です。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111414/6d9ef8c2-c875-b9f5-ab14-b58d66f86964.png)

スマホにするとこんな感じです。
文字サイズとかいい感じに変わってくれるところとか素晴らしいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111414/ca47586c-fdb3-379f-39fa-8f54aebe1e1c.png)

ダークモードにするとこんな感じです。
本来はOSの設定に応じて切り替わりますが、Reactの勉強がてら切り替えボタンをつけてみました。文字色もうまく変えてくれています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111414/86725f36-4c5d-d259-72b2-6ee38093f25a.png)

キャプチャではわかりませんが、検索APIを呼び出している間は、ProgressCircleがくるくる回ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111414/5f4b78dd-2a20-346c-23b4-5def575b8490.png)

## 中身の説明
### リセットCSS
リセットCSSがなくても大丈夫かもしれませんが、hタグを使った時の余白が気に入らなかったので、リセットCSSを入れました。使用したのはmodern-css-resetです。はじめは、destyle.cssを使ってみたのですが、hタグで見た目の違いが表現できなくなってしまったので、適度にリセットしてくれるmodern-css-resetにしました。導入は簡単で、`yarn add modern-css-reset`して、index.jsに`import 'modern-css-reset';`するだけです。

### Flex,Gridで簡単にレイアウト
App.jsでレイアウトを作っていきます。レイアウトを実現するのにCSSを自分で書く必要はなく、用意されたタグだけで十分でした。
全体の構成は

- ヘッダー
- 検索部分
- ガイダンス部分
- コンテンツ部分
- フッター

です。
全体をFlexboxで並べて、それぞれのレイアウトの調整にさらにFlexboxやGridを使いました。コンテンツ部分は固定サイズで配置したかったのでGridにしています。React Spectrumのイメージがつかめるように、import部分とreturnのレイアウト部分のみ抜粋します。

```App.js
import React, { useReducer, useEffect } from 'react';
import './App.css';
import HookedHeader from "./HookedHeader";
import Movie from "./Movie";
import Search from "./Search";
import { Provider, defaultTheme, Flex, View, Text, Grid, repeat, Footer, ProgressCircle, Link } from '@adobe/react-spectrum';


const App = () => {

  return (
    <Provider theme={defaultTheme}
      colorScheme={colorSchemeStete.colorScheme}>
        
      {/* 全体をflexbox化する */}
      {/* ダークモードでも白地が見えないように画面の高さ分をコンテンツ領域で確保する */}
      <Flex direction="column" gap="size-100" minHeight="100vh">

        {/* ヘッダー部 */}
        <HookedHeader
          text="HOOKED"
          switch={switchColorScheme}
          currentColorScheme={colorSchemeStete.colorScheme} />

        {/* 検索部 中央寄せにする*/}
        <Flex direction="row" justifyContent="center">
          <Search search={searchMethod} />
        </Flex>

        {/* ガイダンス部 中央寄せにする */}
        <Flex direction="row" justifyContent="center">
          <Text>Sharing a fwe of our favourite movies</Text>
        </Flex>

        {/* コンテンツ部 Grid化する */}
        <Grid
          columns={repeat('auto-fit', 'size-2400')}
          autoRows="size-2400"
          justifyContent="center"
          gap="size-200">
          {loading && !errorMessage ? (
            // ローディング表示
            <View
              // 上下中央表示
              alignSelf="center"
              // 左右中央表示（gridのrpeatを無視）
              justifySelf="center">
              <ProgressCircle aria-label="Loading…" isIndeterminate />
            </View>
          ) : errorMessage ? (
            // エラーメッセージ表示
            <View
              // 左右中央表示（gridのrpeatを無視）
              justifySelf="center">
              <div className="errorMessage">{errorMessage}</div>
            </View>
          ) : (
                // コンテンツ表示
                movies.map((movie, index) => (
                  <View
                    backgroundColor="gray-200">
                    <Movie key={`${index}-${movie.Title}`} movie={movie} />
                  </View>
                ))
              )}


        </Grid>

        {/* フッター リンクをつけてみる*/}
        <Footer alignSelf="center">
          {/* Linkを使うとダークモードでも見やすく色が変わる */}
          <Link>
            <a href="https://www.freecodecamp.org/news/how-to-build-a-movie-search-app-using-react-hooks-24eb72ddfaf7/" target="_blank">
              freeCodeCamp：How to build a movie search app using React Hooks
          </a>
          </Link>
          <br />
          <Link>
            <a href="https://react-spectrum.adobe.com/react-spectrum/index.html" target="_blank">
              React Spectrum：A React implementation of Spectrum, Adobe’s design system.
          </a>
          </Link>
        </Footer>
      </Flex>
    </Provider>
  );
};

export default App;
```
ちなみにヘッダーコンポーネントも中身の配置を制御するのに、flexbox化しています。

```HookedHeader.js
import React from "react";
import { Header, Heading, View, Flex } from '@adobe/react-spectrum';
import ColorSchemeSwitch from './ColorSchemeSwitch';

const HookedHeader = (props) => {
  return (
    <Header>
      {/* 背景色 */}
      <View backgroundColor="red-500">
        {/* 文言を中央寄せ */}
        <Flex direction="row" justifyContent="center">
          {/* h2と同等 */}
          <Heading level="2" >
            <font color="white">{props.text}</font>
          </Heading>
          <View position="absolute" right="size-0">
            <ColorSchemeSwitch
              switch={props.switch}
              currentColorScheme={props.currentColorScheme}>
            </ColorSchemeSwitch>
          </View>
        </Flex>
      </View>
    </Header>
  );
};

export default HookedHeader;
```

いちいち、classNameをつけて、cssを開いてflexにして、うまくいかないから、divを追加してみて、、、なんてことをしなくても、`<Flex>`タグや`<Grid>`タグを並べていけばいいんです！しかも、設定すべき項目はパラメータ化してあるので、directionやjustifyContent、gapなど細かい調整もjsファイルだけでできてしまいます。

### プロパティの統一感
マニュアルの見やすさからくるものかもしれませんが、コンポーネントに指定できるプロパティが、

- 固有のプロパティ
- Event
- Layout
- Spacing
- Sizing
- Background
- Borders
- Positioning
- Accessibility
- Advanced

などに体系だって定義されています。コンポーネントごとに、利用できる分類は異なりますが、分類の中では基本的に同じ内容のようです（Eventは違う）。すぐに覚えて使えるようになれました。

また、地味にいいのが、`DimensionValue`です。sizeなどを`12px`とか`1rem`とか指定するのではなく、Spectrumで定義された`DimensionValue`を使って指定します。具体的には、`size-0`、`size-10`、`size-25`・・・といった値です。これらを使っていれば、Spectrum側で、デバイスに合わせた調整などを行ってくれます。指定する側もpxを使うかremを使うかemを使うかいちいち悩まなくて済みますね。同様に色指定も`ColorValue`があるので簡単に指定できます。

### 文字色の指定は？
使っていて唯一わからなかったのは、文字色を自分で指定したいときです。ここはこの色にしたい！っていう箇所はやっぱり出てくるのではないかと思います。バックグラウンドカラーを変更するプロパティはあったのですが、文字色を変えるプロパティはぱっと見、見当たりませんでした。classを指定してCSSで書くというのは最後の手段のようなので、一旦、`<font>`タグで色を指定したのですが、これだと、`ColorValue`も使えませんし、正しいやり方のようには思いません。そもそも、意味なく文字色だけ変えるなってことですかね。

# おわりに
Ract Spectrumはリリースされたばかりということで、まだ情報が少ないですが、公式のマニュアルが充実しているので特段困ることはなかったです。React歴数時間、CSSフレームワーク等はBootstrapを眺めたことがある程度ですが、非常に簡単でした。CSSを書かなくてよくて、且つ、タグも自然と標準化された書き方になっていくので、ストレスなく書けました。人気がでることに期待です！

# 参考
[React Spectrum](https://react-spectrum.adobe.com/react-spectrum/index.html)
[freecodecamp：Build a movie search app using React hooks](https://www.freecodecamp.org/news/how-to-build-a-movie-search-app-using-react-hooks-24eb72ddfaf7/)