Reactで誰もがやりたかった10の機能。アプリ構想はあるけど作れない人の壁をぶっ壊す。

この記事は

- **Reactで今どきなWebアプリ**を作りたい（あわよくばサーバーレスにしたい）
- チュートリアルは触ってみたけど、その後はどうしたら良いのか**右も左も分からない**

という人向けに、**「こんな感じで実装したら公開できるアプリとしての体裁は整いそうですぞ」**という**オレオレHowToをまとめたもの**です。


　
**ーー追記（2019/4）ーー**
この記事は2018/9に作ったものなので、バージョン周りなど結構古くなっちゃってます。MaterialUIなどは今はV3が出ていてドキュメントを見る時などご注意いただければと思います。参考程度にご活用下さい。
　


まとめてたら少し長くなってしまったのですが、とりあえず動くソースだけくれれば良いよって人は、[**GitHubでソースも全て公開（MIT License）**](https://github.com/ykawase1011/animel)もしてますので```git clone```するなりご自由にどうぞ。

※最近はVue系のNuxtでこんなこともやってみたので、興味ある方はこちらも是非。
[Webアプリ無料運営のススメ：FirebaseとNuxt(Vue)なら最強！](https://qiita.com/y_kawase/items/fb9f747d88af151f981a)

## 今回作ったレスポンシブSPA【Animel | アニメなにみる？】
[![animel-all.gif](https://qiita-image-store.s3.amazonaws.com/0/262161/99fb277e-42a6-61d5-e573-07c8b9f3c6b3.gif)](https://animel.f-arts.work/)
こちらから実際に触れます→ https://animel.f-arts.work/

**スマホ・PCに対応した、シングルページアプリケーション（SPA）**です。
ボタンを押下すると[ShangriLa Anime API](https://qiita.com/AKB428/items/64938febfd4dcf6ea698)を叩いて今期のアニメ情報を取得してきて表示するというシンプルなアプリ。昨今はモバイルファーストの世の中なのでモバイルを意識しつつもPCで表示できるようにレスポンシブなアプリにしています。

※今回は簡単のため、Firebase使ったログイン機能とかの若干込み入った内容は今回は無し。まずは簡単なところから着実に。

[こちら](https://animel.f-arts.work/)で実際に触れます。また、[GitHubでソース公開（MIT License）](https://github.com/ykawase1011/animel)してますので使いたい人はどうぞ。こんなカスタマイズしてみたらもっと便利だよとか是非教えて下さい。React-v16・react-router-v4・Material-UI-v1など最新のものをわりかし使いました。


## 自己紹介・今回の趣旨
普段はRuby on Railsとか使ってアプリをつくってます。最近だと、Railsで[**歌声専門フリマサイト**](https://utaile.com/)とかいう超ニッチサービスを立ち上げたりしました。また、Rails・WordPress・AWS系のネタで[**最近ブログにTIPSまとめている**](https://blog.f-arts.work/)ので宜しければこちらも是非。

[**メンターマッチングサービス「MENTA」**](https://menta.work/plan/933)にも登録しておりますので、**直接ビデオ通話で技術系相談したいぞ**という方は、こちらも良ければご活用下さい。答えられることなら何でも答えます。
https://menta.work/plan/933

このようにRailsを割と好んできたのですが、昨今のサーバーレスの流れを考えると、Railsは少し立ち回りにくいように感じ、**Lambda**とか**Firebase**とかを上手く利用して、**スケーラビリティやコスト最適化をしていきたいな**と思い、ひとまずフロントのReactに手を出し始めました。

ただ、Reactで今どきのWebアプリ作りたくてチュートリアルをやったはいいのですが、実際のアプリに落とし込む壁を若干感じてました。「結局のところ何のパッケージ入れればいいんだ」とか「チュートリアルから先をどうすればそれなりのアプリになるのか…」といった方多いのではないでしょうか。私自身もそこがしんどかったので、同じ境遇の人がいるのではないかと思い今回まとめてみました。私自身もReact勉強中の身ですので不備不足はご容赦下さい。**React情強の方のアドバイス・ご指摘など大歓迎**です…！\:ok_woman:\:ok_woman:\:ok_woman:

本記事は、「**Reactチュートリアル**」と「**ちゃんとしたアプリ**」の間にある**壁を崩していきたい**というつもりで、誰もがおそらくやりたいと考える**【誰もがやりたかった10の機能】**を軸に書きました。ざっくり書いているので、詳細は[今回のアプリのソース](https://github.com/ykawase1011/animel)で確認してみてください。

なお、チュートリアルがまだの方は、[**React公式の○×ゲーム**（Tutorial: Intro to React）](https://reactjs.org/tutorial/tutorial.html)とか、[**電卓アプリで学ぶReact/Redux入門(実装編)**](https://qiita.com/nishina555/items/9ff744a897af8ed1679b)とかやるととても勉強になりましたのでそちらを是非。ちなみに公式チュートリアル動かしてみた感じや所感も私自身まとめてみたので[**こちらの記事も是非ご参考**](https://blog.f-arts.work/archives/745)ください。

## 誰もがやりたかった10の機能
**\:raised_hand: 大目標【公開できる体裁の整ったアプリ】\:raised_hand:**

今回はチュートリアル以上、ガチSPA以下を目指しているので、このくらいの機能がついていれば及第点かなというアプリを目指します。公開するならこの機能は欲しいなと私が考える10の機能は以下の通り。

- **基本機能**
    - URLと連動した表示（react-router）
    - SNSシェア・クリップボードコピーボタンの設置（react-share・react-copy-to-clipboard）
    - ネイティブアプリ感のあるスクロールバー設置（react-custom-scrollbars）
- **複雑な機能（状態管理・API）**
    - アニメデータ・通知データをReduxで状態管理（Redux）
    - APIを叩いて画面に結果を表示する（Redux＋redux-thunk＋axios）
- **見た目（Material-UI）**
    - ヘッダー・フッターナビゲーションをレスポンシブに（ResponsiveDrawer・BottomNavigation）
    - フッターナビゲーションの選択ボタンがURLと連動（BottomNavigationとreact-routerの連携）
    - 通知バーの表示（Snackbar）
- **トラッキング・広告**
    - Google Analystics導入（react-ga）
    - Google Adsense導入（react-adsense）

今回はこの10項目に的を当てて書いていきます。細かいSEO対策とかは別途ググるといいですが、一応[public/index.html](https://github.com/ykawase1011/animel/blob/master/public/index.html)にそれとなくメタタグは埋め込んでるので参考にはどうぞ。

## ディレクトリ構成・ソフトウェア構成
```
$ cd animel/src
$ tree --dirsfirst -v
.
├── actions
│   └── index.js（Actionを列挙（アニメAPI叩くなど））
├── components
│   ├── Anime.js（アニメリストを作るための個々のカード）
│   ├── NotificationSnackbar.js（通知用のSnackbar）
│   ├── ResponsiveDrawerListItem.js（ヘッダメニュー用）
│   ├── WithTracker.js（Google Analysticsを各ページに設定するためのラッパー）
│   └── WrapMainContent.js（メインコンテンツ（Home、Info、Settings）のラッパー）
├── containers
│   ├── AnimeList.js（アニメリストを作る）
│   ├── Home.js（/home画面）
│   ├── Info.js（/info画面）
│   ├── Notification.js（通知用）
│   ├── ResponsiveDrawer.js（ヘッダメニュー＋表示画面の土台）
│   ├── RouteRelatedBottomNavigation.js（フッターのナビゲーションボタン）
│   ├── Settings.js（/settings画面）
│   └── ShareDialog.js（右上シェアボタンポップアップ）
├── reducers
│   ├── AnimeListReducer.js（アニメ情報管理）
│   ├── NotificationReducer.js（通知情報管理）
│   └── index.js（reducerまとめ）
├── utils
│   └── actionTypes.js（Actionの種類を列挙）
├── App.css
├── App.js
├── index.css
├── index.js
└── registerServiceWorker.js
```
GitHub：https://github.com/ykawase1011/animel

構成は[電卓アプリで学ぶReact/Redux入門(実装編)](https://qiita.com/nishina555/items/9ff744a897af8ed1679b)を参考にさせていただきました。ディレクトリ配置などの詳しい説明はそちらをご参考ください。今回のアプリは

- React土台
    - index.js・App.js
- 全画面共通要素
    - ヘッダとコンテンツ画面部分：ResponsiveDrawer.js
    - ボトムナビゲーションボタン：RouteRelatedBottomNavigation.js
    - 通知エリア：Notification.js
- 画面は3つ
    - ルート画面：Home.js
    - 詳細画面：Info.js
    - 設定画面：Settings.js

を軸としており、それぞれの要素から細かいコンポーネントやアクションなどを呼び出しています。なお、使ったパッケージは下記です。詳細は[package.json](https://github.com/ykawase1011/animel/blob/master/package.json)にて

```
react：16.4.2（React）
react-router：4.3.1（URL遷移本体）
react-router-dom：4.3.1（URL遷移（Web））
redux：4.0.0（Redux）
react-redux：5.0.7（Redux × React）
redux-thunk：2.3.（Redux × API）
material-ui：1.5.1（マテリアルデザインの見た目に）
axios：0.18.0（API叩く）
react-copy-to-clipboard：5.0.1（クリップボードにコピー）
react-custom-scrollbars：4.2.1（ネイティブっぽいスクロールバー）
react-share：2.3.1（SNSシェアボタン）
react-ga：2.5.3（Google Analystics）
react-adsense：0.0.6（Google Adsense）
```

まえがきが長くなりましたが、さっそく**【誰もがやりたかった10の機能】**を軸に、**難しかった点・解決策・詰まった点など**まとめます。

# 基本機能
## 1. URLと連動した表示（react-router）
react-routerを使うことで、URLで画面情報を変える事ができるようになります。URLは色々変わるけど実際は内部でjsが色々頑張っている…これぞSPAの醍醐味感がありますね。react-routerを使うためには、```index.js```と```App.js```に書いている```<Router> <Switch> <Route>```などを使って色々書いてく必要があります。

**\:dog: ワンポイント**
react-routerがv3からv4で仕様が結構変わっているという点に注意。調べ物の際にはバージョンに気をつけて下さい。
[react-router公式ドキュメント](https://reacttraining.com/react-router/web/guides/philosophy)

```react:index.js
// ソースより抜粋。詳細はソースにて。
import { BrowserRouter as Router } from 'react-router-dom';

ReactDOM.render(
      <Router> {/* このRouter（もしくはBrowserRouter）で囲む */}
        <App/>
      </Router> {/* このRouter（もしくはBrowserRouter）で囲む */}
  , document.getElementById('root'));
registerServiceWorker();
```

```react:App.js
// ソースより抜粋。詳細はソースにて。
import { Route, Switch } from 'react-router-dom';

// 不明なRouteは全てNotFound
const NotFound = () => {
  return(
    <h2>ページが見つかりません</h2>
  )
}

class App extends Component {
  render() {
    return (
          {/* Switchで囲むとURLにマッチされた最初の<Route>だけレンダリング */}
          <Switch>
            {/* URLでマッチさせたい要素を書いていく */}
            {/* component={Home}とかでもOK。今回はWrapMainContentでラッパーしている。詳細後述。 */}
            {/* exactを入れることで厳密なURL比較が可能に */}
            <Route exact path="/" component={WrapMainContent(Home)} />} />
            <Route exact path="/info" component={WrapMainContent(Info)}/>
            <Route exact path="/settings" component={WrapMainContent(Settings)}/>
            {/* URLヒットしないときはNot Found画面を表示する */}
            <Route component={WrapMainContent(NotFound)}/>
          </Switch>
    );
  }
}
```

これで遷移の土台ができたのであとは遷移させるだけです。下記コマンドでreact-router用のリンクが生成されます。ボタンなどに組み込む時もこのLinkで挟んだり、componentに組み込むと良いです。```import { Link } from 'react-router-dom';```をお忘れなく。

```react
<Link to="/info">Info</Link>
```

## 2. SNSシェア・クリップボードコピーボタンの設置（react-share・react-copy-to-clipboard）
右上のボタンをクリックすると、SNSシェア用のポップアップがでるようにします。
![animel-2-snsshare.gif](https://qiita-image-store.s3.amazonaws.com/0/262161/5b0f7fe1-3e10-6c1b-db84-37b4f6cdcbda.gif)

**\:dog: ワンポイント**

- Material-UIのDialogによるポップアップ機能
- react-shareのSNS投稿ボタン機能
- react-copy-to-clipboardのクリップボードへのURLコピー機能

を```src/ShareDialog.js```につめ、ポップアップ自体の状態管理については、ここはReduxで管理せず、```ResponsiveDrawer.js```で```shareDialogOpen```というstateをもち、```shareDialogToggle```でポップアップオンオフを管理するような仕組みにしました。

URLをコピーが完了した時には、actionで通知情報を更新することで、```<Notification>```の表示がONになります。Notificationについては[8. 通知バーの表示（Snackbar）](#8-通知バーの表示snackbar)にて。

## 3. ネイティブアプリ感のあるスクロールバー設置（react-custom-scrollbars）
そもそもこれは入れるつもりはなく、表示コンテンツを縦長にして、ブラウザデフォルトのスクロールバーでいいやと思っていたのですが、Material-UIの**ヘッダメニューオープン時に表示崩れがおきる**（スクロールバーの横幅分が飛び出てしまったりする）ため入れることにしました。

スクロール系のパッケージはいくつかありましたが、使ってみて一番しっくりきた**react-custom-scrollbars**を使うことにしました。（ただこちらは更新が最近ないのでそういう意味ではreact-scrollとかreact-perfect-scrollbarの方が良いかもです）

- [react-scroll](https://github.com/fisshy/react-scroll)：PCのスクロールバー風
- [react-custom-scrollbars](https://github.com/malte-wessel/react-custom-scrollbars)：スマホネイティブスクロール風
- [react-perfect-scrollbar](https://github.com/goldenyz/react-perfect-scrollbar)：スマホネイティブスクロール風だけどちょっとカクつくような気も（ただ[perfect-scrollbar](https://github.com/utatti/perfect-scrollbar#install)というわりかし有名なのを土台にしてるのでアリだとはおもいます）

**\:dog: ワンポイント**
結構詰まった箇所としては、肝心の```<Scrollbar>```をどこに配置するかでした。
特に、共通描画部分に書いてしまうと、Scrollbar自体が全画面共通になってしまい、遷移先画面で遷移前のスクロール情報を持ったまま移動してしまうこと（画面遷移したときに既に遷移先コンテンツがスクロールしているような状態）がありました。

また、Scrollbarの親要素の高さがスクロールバーの領域になるので、表示したい領域（親要素）から大きく外れた部分には配置できません。

そこで、今回は、**Routeで呼び出している各component「Home・Info・Settings」のラッパーを用意する**ことで解消しました。

```react
<Route exact path="/" component={Home} />
↓↓↓ 自作ラッパーを絡ませる ↓↓↓
<Route exact path="/" component={WrapMainContent(Home)} />
```

```react:WrapMainContent.js
// スクロールバー設定
import { Scrollbars } from 'react-custom-scrollbars';

const WrapMainContent = (WrappedComponent, options = {}) => {
  const HOC = class extends React.Component {
    render() {
      return (
        <Scrollbars>
            <WrappedComponent {...this.props} />
        </Scrollbars>
      );
    }
  };
```

共通化したかったのでラッパーしただけですので、それぞれのComponentのrender部分で直接```<Scrollbar>```で囲んでもOKです。

# 複雑な機能（状態管理・API）
## 4. アニメデータ・通知データをReduxで状態管理（Redux）
アニメデータを```reducers/AnimeListReducer.js```で、通知データを```reducers/NotificationReducer.js```で管理しています。actionについては、```acations/index.js```で定義しています。

APIを叩くところについては、[React + ReduxでREST APIを叩いてリスト表示する方法](https://qiita.com/kazmaw/items/a2def8978127ffb11f92)を参考にさせていただきました。

**アニメデータ状態管理**

- AnimeListReducerとaction
    - 初期状態
        - isFetching: false
        - items: []
    - アクション
        - getAnimes（引数：year（西暦）、cours（クール））
            1. ```dispatch(getAnimesRequest)```で```isFeching```フラグを```true```に。
            2. ```axios.get('https://api.moemoe.tokyo/anime/v1/master/' + year + '/' + cours)```でAPIリクエストを投げる。
            3. 成功すれば```dispatch(getAnimesSuccess)```、失敗すれば```dispatch(getAnimesFailure)```をします。
        - getAnimesRequest
            1. ```isFeching```フラグを```true```にし、```items```を空にする。
        - getAnimesSuccess（引数：json）
            1. ```isFeching```フラグを```false```にし、```items```にAPIで取得してきたjsonデータを入れる。
        - getAnimesFailure（引数：error）
            1. ```isFeching```フラグを```false```にし、errorを返す。

**通知データ状態管理**

- NotificationReducerとaction
    - 初期状態
        - isOpen: false
        - variant: 'success'
        - message: ''
    - アクション
        - setNotification（引数：variant（通知の種類）、message（通知メッセージ））
            1. ```dispatch(getAnimesRequest)```で```isFeching```フラグを```true```に。
            2. axiosでAPIリクエストを投げる。
            3. 成功すれば```dispatch(getAnimesSuccess)```、失敗すれば```dispatch(getAnimesFailure)```をします。



## 5. APIを叩いて画面に結果を表示する（Redux＋redux-thunk＋axios）
上で定義したactionなどを叩いていきます。上では説明を省いていたのですが、ReduxではActionで非同期呼び出しなどはNGのようなので、redux-thunkというミドルウェアを使うようにします。

細かい話は[React + ReduxでREST APIを叩いてリスト表示する方法](https://qiita.com/kazmaw/items/a2def8978127ffb11f92)を読むのが早いと思います。

今回はトップページのボタンをクリックするとアニメAPIを叩いてデータを取得してくるというのを下記のように実現しました。```Button```の```onClick```イベントに、先程定義した```getAnimes```を実行するように設定してあげる感じ。

```react:Home.js
<Button onClick={() => actions.getAnimes(this.state.year, this.state.cour)}>
  {this.state.year}年（{cours_detail_month[this.state.cour-1]}）<br/>のアニメを検索
</Button>
```


Redux使ってるので、忘れずにconnectとかしてあげましょう。

```react:Home.js
// Material-ui関連
Home.propTypes = {
  classes: PropTypes.object.isRequired,
  theme: PropTypes.object.isRequired,
};

// Redux関連
const mapState = (state, ownProps) => ({
});
function mapDispatch(dispatch) {
  return {
    actions: bindActionCreators(actions, dispatch),
  };
}

// Material-uiのテーマ設定＋Redux設定
export default connect(mapState, mapDispatch)(
  withStyles(styles, { withTheme: true })(Home)
);
```



# 見た目（Material-UI）
Material-UIが使いたいためにReactを始める人がいるくらい、素晴らしい出来です。
ネイティブアプリ感のある仕上がりに簡単になるので、さくっとWebアプリを作りたい場合は導入しましょう。
現在**v1と旧バージョンがあり情報が混在**しているので注意。今回は最新v1を使っています。
https://material-ui.com/

## 6. ヘッダー・フッターナビゲーションをレスポンシブに（ResponsiveDrawer・BottomNavigation）
### ヘッダー
レスポンシブなヘッダが既に用意されています。素晴らしい。
https://material-ui.com/demos/drawers/#responsive-drawer

私は、各リストアイテムをコンポーネント化してしまったのですが、しなくても全然OKです。さて、実用を考えると、このリストをボタンにしたいと思うので、こんな感じでLinkを差し込み改造します。

```react
  <ListItem button component={Link} to='/' onClick={this.closeDrawerNav}>
    <ListItemIcon>
      <HomeIcon />
    </ListItemIcon>
    <ListItemText primary='トップページ' />
  </ListItem>
```

```react-router-dom```の```Link```を使うので、```import { Link } from 'react-router-dom';```をお忘れなく。

また、クリックしたときにちゃんとメニューを閉じて欲しいので、```closeDrawerNav```というのも用意しました。

```react
class ResponsiveDrawer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      mobileOpen: false,
      shareDialogOpen: false,
    };
  }

  closeDrawerNav = () => {
    this.setState({ mobileOpen: false });
  }
```

細かいスタイル等はソースを参考にしてください。

### フッター
これもMaterial-UIで良いのが既に用意されています。素晴らしい。
https://material-ui.com/demos/bottom-navigation/#bottom-navigation



ちなみにフッター固定は、適当にCSS検索して画面下に配置されるようにしただけです。

```react:RouteRelatedBottomNavigation.js
// スタイル
const styles = theme => ({
  wrapper:{
    display: 'block',
    width: '100%',
    position: 'fixed',
    left: 0,
    bottom: 0,
    zIndex: 1000,
    textAlign: 'center',
  },
  root: {
    [theme.breakpoints.up('md')]: {
      display: 'none',
    },
  },
  button: {
    maxWidth: '100%', // ボタンが横一杯に広がって欲しくない時はコメントアウト
  },
});
```

なお、画面サイズが大きくなった時はフッターを```display: none```にすることでデスクトップ用になるような工夫をしています。```[theme.breakpoints.up('md')]:```のあたりの処理ですね。

ただし、フッター非表示にすると、フッター分の隙間ができてしまうので、そこは、```ResponsiveDrawer.js```のスタイルでメイン画面部分の下部余白サイズを制御しています。

```react:ResponsiveDrawer.js
// 設定値
const drawerWidth = 240;
const headerNavigationHeight = 56;
const bottomNavigationHeight = 56;
// スタイル
const styles = theme => ({
  中略
  content: {
    flexGrow: 1,
    backgroundColor: theme.palette.background.default,
    padding: theme.spacing.unit * 3,
    paddingTop: `calc(10px + ${headerNavigationHeight}px)`,
    paddingBottom: `calc(10px + ${bottomNavigationHeight}px)`,
    paddingLeft: 0,
    paddingRight: 0,
    [theme.breakpoints.up('md')]: {
      paddingBottom: 10,
    },
  },
});
```

## 7. フッターナビゲーションの選択ボタンがURLと連動（BottomNavigationとreact-routerの連携）
フッターの見た目は上記設定でOKなのですが、ボタン押下時にはボタンが選択状態になるのですが、「ヘッダーから操作した時」や「URLから飛んできたとき」にフッターボタンが選択状態にならなかったりして困ってました。

**\:dog: ワンポイント**
そこで、```react-router```が持っている現在の```location```情報を引き出し、該当するURLにヒットする場合はそのボタンを選択状態にするという実装を追加しました。

まずlocation情報を引き出すためには、```react-router```の```withRouter```を使う必要があります。

```react:RouteRelatedBottomNavigation.js
import { Link, withRouter } from 'react-router-dom';

中略

export default withRouter(
  withStyles(styles, { withTheme: true })(RouteRelatedBottomNavigation)
);
```

さて、実装のポイントです。デフォルト状態だと、ボタン選択にはvalueで1とか2とかを指定して、どのボタンが押されているか判定しています。今回はそのvalueを直接locationとするように修正しています。```<BottomNavigation>```のvalueを```this.props.location.pathname```にして、各ボタン要素の```<BottomNavigationAction>```のvalueをリンクのパスにしているのがポイントです。これによって、URLがヒットする時は該当ボタンを選択状態にして、関係のないリンクの時はボタンは全て未選択状態になります。

今回のアプリだと```/settings```の時は全てのボタンが未選択になります。
https://animel.f-arts.work/settings

```react:RouteRelatedBottomNavigation.js
class RouteRelatedBottomNavigation extends React.Component {
  buttons_info = [
    { label: 'トップページ', icon: <HomeIcon />, link_to: '/'},
    { label: 'Animelとは', icon: <InfoIcon />, link_to: '/info'},
  ];
  
  buttons = this.buttons_info.map( (button_info, index) => {
      return (
        <BottomNavigationAction
          value={button_info.link_to}
          label={button_info.label}
          className={this.props.classes.button}
          icon={button_info.icon}
          component={Link}
          to={button_info.link_to}
        />
      );
    })
  
  render() {
    // Material-ui関連
    const { classes } = this.props;
    
    return (
      <div className={classes.wrapper}>
        <BottomNavigation
          value={this.props.location.pathname}
          showLabels
          className={classes.root}
          children={this.buttons}
        />
      </div>
    );
  }
}
```

## 8. 通知バーの表示（Snackbar）
これもMaterial-UIで良いのが既に用意されています。最the高。
https://material-ui.com/demos/snackbars/#customized-snackbars

通知バーについては、各所から通知を出すことが想定されるので、状態管理をReduxで行い、必要に応じてactionコマンドを叩くような実装にしました。

```react:App.js
class App extends Component {

  render() {
    return (
      <div className="App">
        <Notification/>
        <ResponsiveDrawer className="ResponsiveDrawer">
          中略
        </ResponsiveDrawer>
        <RouteRelatedBottomNavigation/>
      </div>
    );
  }
}
```

こんな感じで配置しておき、使いたいときにStoreを修正することで表示されるようなイメージです。```<Notification/>```の中身はデモと大体同じですが、適宜actionsを叩くようにしたりしています。

```react:Notification.js
      <Snackbar
        anchorOrigin={{
          vertical: 'top',
          horizontal: 'left',
        }}
        open={NotificationReducer.isOpen}
        autoHideDuration={3000}
        onClose={actions.closeNotification}
        className={classes.root}
      >
        <NotificationSnackbar
          onClose={actions.closeNotification}
          variant={NotificationReducer.variant}
          message={NotificationReducer.message}
        />
      </Snackbar>
```

呼び出しのときは、```actions.setNotification('info', 'Hoge message!')```のように呼び出します。

# トラッキング・広告
## 9. Google Analystics導入（react-ga）
SPAの辛いところは、GoogleAnalysticsを導入しようとしても普通にやるとアプリ全体へのアクセスしかカウントされないというデメリットがあります。そこで、react-gaを導入し、ページ遷移に対してAnalysticsのページ移動のカウントをするように改造します。

内容は[react-ga](https://github.com/react-ga/react-ga)に書いてあるように実装し、Routeで呼び出しているcomponentを、```WithTracker```でラッパーするということで実現しています。

なお、```WithTracker```ラッパーの作り方も[こちらを参考](https://github.com/react-ga/react-ga/wiki/React-Router-v4-withTracker)にしました。


## 10. Google Adsense導入（react-adsense）
アプリを作る時に広告を入れたいという方も多いかとは思います。

### 広告ユニットの場合
広告ユニットの場合は、[react-adsense](https://github.com/hustcc/react-adsense)を使いました。

入れたいところに

```react
<AdSense.Google
  client='ca-pub-7292810486004926'
  slot='7806394673'
  style={{ display: 'block' }}
  format='auto'
  responsive='true'
/>
```
のようにして入れていきます。ちなみに今回の私の設定では、全ての画面でコンテンツ下部に表示をしたかったので、コンテンツ画面のラッパー```WrapMainContent```（スクロールバーなどもラップしている）にまとめて追記しました。

```react:components/WrapMainContent.js
        <Scrollbars>
          
          {/* classNameでwrapperとfooterを指定することで、footer（今回はAdsense）をページ下部に貼り付けることが出来る（見た目用途） */}
          <div className={classes.wrapper}>

            <WrappedComponent {...this.props} />
            
            {/* Google Adsense設定。詳細はこちら：https://github.com/hustcc/react-adsense */}
            {/* clientやstlotは私の設定なので実際は各自の値を利用すること */}
            <div className={classes.footer}>
              <AdSense.Google
                client='ca-pub-3774224802378126'
                slot='5432750074'
                style={{ display: 'block' }}
                format='auto'
                responsive='true'
              />
            </div>
          </div>
        </Scrollbars>
```

### 自動広告の場合
Googleの自動広告を入れたい時の設定は[react-adsense](https://github.com/hustcc/react-adsense)には見た感じなさそうなので、こちらを導入したい人は、普通の設定どおり、```public/index.html```のhead内に指定コードを入れるといいのかと思います。

試しに自動広告を入れてみたのですが、アンカー広告（スマホの下部にでる広告）は、**フッターナビゲーションとは相性はあまり良くなさそう**ですね…（そういうトラップ広告として出すなら最強ですが、最近は無効トラフィックとかで検知されちゃいそうですよねｗ）

アンカー広告は、中央に広告のぴょこっとしたものがあるので、フッターナビゲーションのボタンが3個とかの時は真ん中がかぶりそう…

![img_5b8e9cd9878f3.png](https://qiita-image-store.s3.amazonaws.com/0/262161/d145040d-5550-928d-6679-0433d5e3c45a.png)

**フッターナビゲーション入れる場合はアンカー広告は出ないように設定**しておくほうが良いかもしれません。もしくは**アンカー広告が出てる時は、一緒にフッターナビゲーションも上にずらす**とかでしょうか。あと、アンカー広告が出ると少し表示崩れ（ページ下部に変な余白が出来る）がありますね…コンテンツ高さを100vhで考えているのでその弊害っぽい。別のやり方しなきゃ難しいかもですね…なにか良い案ないかしら…


# TIPS・メモ
### aタグ（target="_blank"）を入れる時
```react
<a href="https://github.com/ykawase1011/animel" target="_blank" rel="noopener noreferrer">GitHub</a>
```
のように```rel="noopener noreferrer"```を入れておかないと、警告文が出ます。

### 公開するときにやったこと・ロゴづくりなど裏方作業についてのメモ
FirebaseのHostingとか、ロゴジェネレーターとか使いました。
[こちらにまとめております](https://blog.f-arts.work/archives/791)ので参考に。


# 分からなかったこと\:scream:
実装してて下記がまだ躓いてたりします。もし「解決したよ」「知ってるよ」「教えてあげてもいいよ」って方いたら是非…

### ①react-routerの遷移で毎回コンポーネントが再マウントされる
Routeでの遷移なのですが、毎回コンポーネントがre-mount（遷移毎にcomponentWillMountが発生し、updateにならない）されてしまい、描画負荷がかかっているようでした。

例えば、ルート画面（HOME）からAnimelとは（Info）に移動してまたルート画面（HOME）に戻ると、一からコンポーネント準備をするので、アニメデータが多いとカード描画が200msくらいかかってしまうという現象が発生してしまいました…また、GoogleAdsenseの広告もこのままだと毎回ロードされてしまうので少しやりにくそう。

公式にも書いてたった方法でRouteをcomponent→renderにするなど色々対処をしたのですが結局毎回再マウントされてしまい問題解決しなかったので、もし対処法ご存知の方いらっしゃいましたら是非ご教示いただきたく…

### ②スマホでスクロールをする時、アドレスバーが消えない
これは、react-custom-scrollで、画面中央の部分をスクロールするようにしていることもあり、画面中央の部分的なスクロールが優先されるため、スマホの上部にあるアドレスバーが消えない現象があります。（ヘッダでスクロールすればOKだが、画面中央コンテンツ部分でスクロールするとNG）

アドレスバーが消える条件は、画面全体のスクロールが発生した時ということみたい。なので、```window.onload```イベントで、```window.scrollTo(0,1);```こんなものを入れればいいよ的な記事をみてやってみたのですが、私のスマホでは、アドレスバーにヘッダーがめり込んでしまいました…

Bestな動きとしては、「react-custom-scrollが下スクロール開始したときに、アドレスバーが存在すれば先にwindowのスクロールを行いアドレスバーを隠してからreact-custom-scrollが下スクロールするように…」なんですが…

# まとめ
ここまで読んで頂きありがとうございました。色々書いていたら長くなってしまいました。Reactを触ってみた感じたのは、学習コストは若干高いけど、一度学習すればその後のアプリ作成はババーッとできるようになる気がしてワクワクしています。


## 勉強するときのオススメ

### Reactドキュメント
公式ドキュメント。最近日本語対応したみたい。
https://ja.reactjs.org/docs/getting-started.html

まずはチュートリアル動かす所からが良いかと
https://ja.reactjs.org/tutorial/tutorial.html

### Material-UI
公式ドキュメント。若干読みにくいので頑張ってほしい。主にどんなコンポーネントがあるかとか、どういう使い方してるのかを調べる時に。

### 書籍：React.js&Next.js超入門
掌田先生著。Reactの基礎に加えて今回は取り扱っていないNextの内容もあります。また大変便利なBaaSのFirebaseについても扱っているので、今どきの作り方をガッと体系的に勉強したい時などに大変良い。
[<img src="https://qiita-image-store.s3.amazonaws.com/0/262161/98806192-3d1b-471f-b432-6486c9363774.jpeg" alt="image" width="30%">](https://amzn.to/2U38dnd)

### 書籍：Reactビギナーズガイド
ちょっと古いけど、オライリーから出ているビギナー本。
[<img src="https://qiita-image-store.s3.amazonaws.com/0/262161/4b2ec4b3-b9d7-2973-458a-6bd01cf802d1.jpeg" alt="image" width="30%">](https://amzn.to/2UjzFlq)

## おわりに
最近始めたばかりですが、[**ブログでRuby on Rails・WordPress・AWS系のネタ**](https://blog.f-arts.work/)もしたためております。こちらも宜しければどうぞ。→ https://blog.f-arts.work/

[**メンターマッチングサービス「MENTA」**](https://menta.work/plan/933)にも登録しておりますので、**直接ビデオ通話で技術系相談したいぞ**という方は、こちらも良ければご活用下さい。答えられることなら何でも答えます。「何でもは知らないわよ。知ってることだけ。」
https://menta.work/plan/933

**※追記**
今回初投稿にも関わらず、この記事を多くの方に読んで頂き大変感謝です。私自身まだReact初学者なので至らぬ点もあるとは思いますが、煮るなり焼くなり好きに活用いただいて皆様のアプリ作りの起爆剤になれれば嬉しいです。


#### 過去記事・関連記事も宜しければどうぞ
VueやFirebase関連などをよく書いています。

- [Webアプリ無料運営のススメ：FirebaseとNuxt(Vue)なら最強！](https://qiita.com/y_kawase/items/fb9f747d88af151f981a)
- [Firebaseで完結するリッチなWebアプリ構築の勘所](https://qiita.com/y_kawase/items/1de690b40553ef76acb3)
- [Qiita初投稿でも狙ってバズらせられた話『5つの成功と2つの失敗』](https://blog.f-arts.work/archives/857)

ゆるいネタで**noteも始めてみた**ので、もし興味があればこちらもご覧いただけると嬉しいです。

- [お前はThunderbolt3を何も知らない。USB Type-Cに翻弄されないための教養。](https://note.mu/ykawase/n/n23deaa2a0ef2)