SPA
React
react-router
redux
material-ui

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

この記事は

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

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

まとめてたら少し長くなってしまったのですが、とりあえず動くソースだけくれれば良いよって人は、GitHubでソースも全て公開(MIT License)もしてますのでgit cloneするなりご自由にどうぞ。

今回作ったレスポンシブSPA【Animel | アニメなにみる?】

animel-all.gif
こちらから実際に触れます→ https://animel.f-arts.work/

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

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

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

自己紹介・今回の趣旨

普段はRuby on Railsとか使ってアプリをつくってます。最近だと、Railsで歌声専門フリマサイトとかいう超ニッチサービスを立ち上げたりしました。また、Rails・WordPress・AWS系のネタで最近ブログにTIPSまとめているので宜しければこちらも是非。

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

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

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

なお、チュートリアルがまだの方は、React公式の○×ゲーム(Tutorial: Intro to React)とか、電卓アプリで学ぶReact/Redux入門(実装編)とかやるととても勉強になりましたのでそちらを是非。ちなみに公式チュートリアル動かしてみた感じや所感も私自身まとめてみたのでこちらの記事も是非ご参考ください。

誰もがやりたかった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にそれとなくメタタグは埋め込んでるので参考にはどうぞ。

ディレクトリ構成・ソフトウェア構成

$ 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入門(実装編)を参考にさせていただきました。ディレクトリ配置などの詳しい説明はそちらをご参考ください。今回のアプリは

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

を軸としており、それぞれの要素から細かいコンポーネントやアクションなどを呼び出しています。なお、使ったパッケージは下記です。詳細は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.jsApp.jsに書いている<Router> <Switch> <Route>などを使って色々書いてく必要があります。

:dog: ワンポイント
react-routerがv3からv4で仕様が結構変わっているという点に注意。調べ物の際にはバージョンに気をつけて下さい。
react-router公式ドキュメント

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

ReactDOM.render(
      <Router> {/* このRouter(もしくはBrowserRouter)で囲む */}
        <App/>
      </Router> {/* このRouter(もしくはBrowserRouter)で囲む */}
  , document.getElementById('root'));
registerServiceWorker();
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';をお忘れなく。

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

2. SNSシェア・クリップボードコピーボタンの設置(react-share・react-copy-to-clipboard)

右上のボタンをクリックすると、SNSシェア用のポップアップがでるようにします。
animel-2-snsshare.gif

:dog: ワンポイント

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

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

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

3. ネイティブアプリ感のあるスクロールバー設置(react-custom-scrollbars)

そもそもこれは入れるつもりはなく、表示コンテンツを縦長にして、ブラウザデフォルトのスクロールバーでいいやと思っていたのですが、Material-UIのヘッダメニューオープン時に表示崩れがおきる(スクロールバーの横幅分が飛び出てしまったりする)ため入れることにしました。

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

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

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

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

<Route exact path="/" component={Home} />
↓↓↓ 自作ラッパーを絡ませる ↓↓↓
<Route exact path="/" component={WrapMainContent(Home)} />
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を叩いてリスト表示する方法を参考にさせていただきました。

アニメデータ状態管理

  • 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を叩いてリスト表示する方法を読むのが早いと思います。

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

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とかしてあげましょう。

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を差し込み改造します。

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

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

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

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検索して画面下に配置されるようにしただけです。

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のスタイルでメイン画面部分の下部余白サイズを制御しています。

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-routerwithRouterを使う必要があります。

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

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コマンドを叩くような実装にしました。

App.js
class App extends Component {

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

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

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に書いてあるように実装し、Routeで呼び出しているcomponentを、WithTrackerでラッパーするということで実現しています。

なお、WithTrackerラッパーの作り方もこちらを参考にしました。

10. Google Adsense導入(react-adsense)

アプリを作る時に広告を入れたいという方も多いかとは思います。

広告ユニットの場合

広告ユニットの場合は、react-adsenseを使いました。

入れたいところに

<AdSense.Google
  client='ca-pub-7292810486004926'
  slot='7806394673'
  style={{ display: 'block' }}
  format='auto'
  responsive='true'
/>

のようにして入れていきます。ちなみに今回の私の設定では、全ての画面でコンテンツ下部に表示をしたかったので、コンテンツ画面のラッパーWrapMainContent(スクロールバーなどもラップしている)にまとめて追記しました。

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には見た感じなさそうなので、こちらを導入したい人は、普通の設定どおり、public/index.htmlのhead内に指定コードを入れるといいのかと思います。

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

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

img_5b8e9cd9878f3.png

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

TIPS・メモ

aタグ(target="_blank")を入れる時

<a href="https://github.com/ykawase1011/animel" target="_blank" rel="noopener noreferrer">GitHub</a>

のようにrel="noopener noreferrer"を入れておかないと、警告文が出ます。

公開するときにやったこと・ロゴづくりなど裏方作業についてのメモ

FirebaseのHostingとか、ロゴジェネレーターとか使いました。
こちらにまとめておりますので参考に。

分からなかったこと: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を触ってみた感じたのは、学習コストは若干高いけど、一度学習すればその後のアプリ作成はババーッとできるようになる気がしてワクワクしています。

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

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

あと、もしよければ今回のQiita記事作りの過程をまとめた、調子に乗った記事も書いたのでぜひ読んでみて下さいw → Qiita初投稿でも狙ってバズらせられた話『5つの成功と2つの失敗』