20190203のReactに関する記事は4件です。

Gatsby.jsで静的なWebサイトをサクッと作る(CSS・SCSS篇)

Gatsby.jsで静的なWebサイトをサクッと作る(インストールからHTML編集篇)の続きです。

今回は、CSS(SCSS)でスタイルをあてるまでを解説します。

SCSSを使えるようにする

そのままでは、SCSSファイルが使えないので、プラグインをインストールします。

$ npm install --save node-sass gatsby-plugin-sass

上記コマンドを実行し、完了したらgatsby-config.jsファイルを作成し、SCSSが使えるように以下の記述をします。

gatsby-config.js
module.exports = {
  plugins: [`gatsby-plugin-sass`]
}

これでSCSSが使えるようになります。

グローバルなスタイルを設定

まずは、リセットCSS等の全ページに適用されるスタイルを設定してみましょう。src/styles/global.scssを作成します。

global.scss
html {
  color: red;
}

続いて、gatsby-browser.jsを作成しsrc/styles/gloabl.cssimportします。

gatsby-browser.js
import "./src/styles/global.scss"

これで、文字が赤くなっているはずです。$ npm run developでローカルサーバーを起動し、確認してみましょう。 

スコープされたスタイルを設定する

Gatsby.js(React)では、CSS Modulesを使用し、CSS(SCSS)をスコープさせることができます。現時点ではindex.jsしか存在しないので無意味ですが、コンポーネントが増えてくると、そのコンポーネントでのみ有効なスタイルをあてることができるので、すごく便利です。

では、src/pages/index.jsでのみ有効なsrc/pages/index.module.scssを作成しましょう。

index.module.scss
.txt {
  color: green;
}

続いて、src/pages/index.jssrc/pages/index.module.scssimportします。

index.js
import React from "react"
import Styles from "./index.module.scss"

export default () => (
  <div>
    <p className={Styles.txt}>Hello</p>
    <p>world!</p>
  </div>
)

index.module.scssに書かれた.txtを、index.jsではStylesというオブジェクトとして扱っていています。.txtStylesのプロパティになっているので、className属性で{Styles.txt}とすると、.txtで設定したCSSプロパティが反映されます。

属性名がclassではなくclassNameになっているのは、classがJavaScriptの予約語で使用できないためです。また、{}で囲うことによりJavaScripを実行できます。

ブラウザを確認すると、Helloのみ緑色になっているはずです。また、ChromeのDevToolsでElementを見てみると、class属性部分がユニークな文字列になっています。

今回はここまで!次回も、乞うご期待!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactにFirebaseを使ったログイン機能を実装する

この記事の内容を一言で

自作の家計簿アプリにFirebaseを用いてログイン機能を作成する

問題

家計簿アプリにログイン機能を実装して、ユーザー別に各項目を保存できるようにしたい

解決策

Firebaseを使用する

解決法

Firebaseについて

FirebaseはGoogleが提供しているサービスの一つ。
Baas(Backend as a service)/mBaas(mobaile Backend as a service)を提供している。
Firebaseを利用することで、フロントエンドの開発者でも簡単にデータベースが必要な作業をシステムに実装することができるように。
データベースに関する知識も必要だけど、今求めていることはそこではないため、今回はFirebaseを用いて問題を解決する。

Firebaseについて: Firebaseの始め方
Baasについて: BaaSからSaaSまで!いま一度、クラウドサービスの違いを整理しよう

Firebaseのドキュメント

Firebase を JavaScript プロジェクトに追加する
Firebase Authentication

Firebaseの導入

まずはPCにFirebaseを導入する。

install.sh
 $ npm install firebase

メモ

npm.shを実行した際、以下のエラーが発生した。

$ npm audit fixを使うことで治せるとあるが、詳細を確認するため$ npm auditを打つ。

セキュリティの問題らしい。
今のバージョンだとセキュリティ上良くないんで、更新してほしいと。
More infoにあるhttps://www.npmjs.com/advisories/725 をみると、バージョンを更新することで解決できると書いてあるので、更新する。
実行したのは以下のコマンド。

yarn.sh
 $ yarn add react-scripts@2.1.3

このあとに、yarn版のFirebaseをインストールするコマンドを実行したら無事解決。

yarn_install.sh
 $ yarn add firebase

ログイン機能の実装

次に、ログイン機能を実装していく。
Firebaseにログインして、アプリの名前などを決める。
それが終わったら、この画面からコードをコピーする。
</>のボタンを押すとコードが表示される。

コードをコピーしたら、firebase用のディレクトリを作成し、コピーしたコードをペーストするファイルを作成する。

copy.sh
 $ mkdir firebase
 $ vim firebase.js
firebase.js
import firebase from 'firebase/app'
import 'firebase/auth'

const config = {
   apiKey: "***",
   authDomain: "***",
   databaseURL: "***",
   projectId: "***",
   storageBucket: "***",
   messagingSenderId: "***"
}
// "***"の部分にコピーしたコードの内容をペーストする。

firebase.initializeApp(config);

export default firebase

続いて、ログイン、ログアウトのボタンを作成していく。

Auth.sh
 $ mkdir Auth
 $ vim Auth.js
Auth.js
import React, { Component } from 'react'
import firebase from './firebase'

class Auth extends Component {
  state = {
    user: null
  }

  componentDidMount() {
    firebase.auth().onAuthStateChanged(user => {
      this.setState({ user })
    })
  }
  //componentDidMountはrenderが実行された後に行われる。データの受け渡しが可能な状態になったら下記のコードが実行されていく。
  //onAuthstateChangeでuserにログインしたユーザーの情報を与える

 login() {
    const provider = new firebase.auth.GoogleAuthProvider()
    firebase.auth().signInWithRedirect(provider)
  }
  //signInWithRedirectでGoogleのログインページに接続して、Google プロバイダ オブジェクトのインスタンスを作成する。
  logout() {
    firebase.auth().signOut()
  }

  render() {
    return (
      <div className="Auth">
        {this.state.user ? (
           <h1 className="UserName">{this.state.user && this.state.user.displayName}の家計簿</h1>
           // displayNameでログインした人のGoogleアカウントに登録されている名前を表示する
        ) : (
           <h1 className="Name">あなたの家計簿</h1>
      //ログインしていない人用の表示
        )}
        {this.state.user ? (
          <button onClick={this.logout}>Google Logout</button>
      //ユーザーがログインしている時はlogoutボタンを表示する
        ) : (
          <button onClick={this.login}>Google Login</button>
      //ユーザーがいない時はloginボタンを表示する
        )}
      </div>
    )
  }
}

export default Auth

こちらのコードのほとんどは10分でできるReact+FirebaseのGoogleAuthから。
自分なりに解釈したものをコメントアウトに記述。

あとは、Auth.jsで作成したコンポーネントをapp.jsに設置し、各自の環境でサーバーを起動して、Firebaseが導入、実行されているかを確認する。

componentDidMountについて: 5分で理解する React.js
GoogleAuthProviderについて: Google ログインと JavaScript を使用して認証する
displayNameについて: Firebase でユーザーを管理する

コードを隠す

firebase.jsのコードをGithubなどのインターネットに挙げるときはAPIキーを隠す。
その方法は、package.jsonといった、プロジェクトのroot階層に.envファイルを作成することでできる。
また、create-react-appを使っている場合、.envに書くのはREACT_APP〜である必要があった。
今回はapiKeymessagingSenderIdを隠してみる。

 REACT_APP_FIREBASE_APIKEY="***"
 REACT_APP_FIREBASE_MESSAGING_SENDER_ID="***"
firebase.js
import firebase from 'firebase/app'
import 'firebase/auth'

const config = {
   apiKey: process.env.FIREBASE_APIKEY,
   authDomain: "***",
   databaseURL: "***",
   projectId: "***",
   storageBucket: "***",
   messagingSenderId: FIREBASE_MESSAGING_SENDER_ID
}

firebase.initializeApp(config);

export default firebase

という風に。
エラーが発生しても、yarn startをすることで認識される場合があるため、確認する。

.envの読み込みについて: create-react-appのプロジェクトで環境変数を.envから読み込めないときに確認すること

まとめ

データベースの知識を用いずにユーザーのログイン機能を実装できるのはメチャクチャ便利。
他にも色々機能あるみたいなので、触りながら理解を進めていきたい。
もし、Auth.jsの解釈で間違っているところがあれば、指摘をお願いします。

Github

Issue: Login function
PullRequest: feat: Create Login Function

参考資料

全般: 10分でできるReact+FirebaseのGoogleAuth
Firebaseについて: Firebaseの始め方
Baasについて: BaaSからSaaSまで!いま一度、クラウドサービスの違いを整理しよう
componentDidMountについて: 5分で理解する React.js
GoogleAuthProviderについて: Google ログインと JavaScript を使用して認証する
displayNameについて: Firebase でユーザーを管理する
.envの読み込みについて: create-react-appのプロジェクトで環境変数を.envから読み込めないときに確認すること

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactアプリでもwebpack-bundle-analyzerでバンドルサイズを可視化する

概要

Reactでいろいろパッケージ入れていくと、いつの間にかバンドルサイズが肥大化してしまいます。
バンドルサイズ削減のため、どのパッケージがどの程度占めているのか可視化したいところです。

これまでCreateReactAppで作成した場合、そのままではWebPackの設定にアクセスできないため、イジェクトする必要がありました。
(イジェクトすると設定ファイルが外に出てくるので自由にできる反面、そこからのメンテは自分たちで責任を持つというトレードオフだったはず)
現在ではイジェクトなしで可視化できるようになったので、それの紹介になります。

読者対象

  • Reactアプリでバンドルサイズをお手軽に可視化したい方。
  • キャッシュが効かない時にアプリの初期表示が遅くて困っている方。

いきなり結論

CreateReactAppのドキュメントに関するプルリクの最初の投稿が全てです(;^_^A
(CreateReactAppのドキュメントがあるのを初めて知ったorz)
https://github.com/facebook/create-react-app/pull/6127
CreateReactAppでサポートしたから、あとはパッケージ入れてちょっとスクリプト実行してねという話です。
執筆時点ではまだドキュメントが更新されていませんが、CreateReactApp自体にはV2時点で入ったのかな?

方法だけ知りたかった方はここまででOKです。
あとはこれに到達したまでの経緯の話になります。

Road to 結論

Reactアプリでwebpack-bundle-analyzerを利用できないかググる

webpack-bundle-analyzerの記事を見かけて、ちょっと入れたいなという欲求がわく。
だけど、CreateReactAppがサポートしていないときついんじゃないかという不安も・・・
ぐぐってみると、ちょっとトリッキーですが単体プログラムとして実行している方のIssuesを見つける。
https://github.com/facebook/create-react-app/issues/3518
後のコメントを見ずにやってみてたんですが、最新ではCreateReactAppのWebPackの設定ファイルの構造が変わって?動かない(;^_^A
ようやく後尾のコメントのリンクを見て、CreateReactAppがサポートしていることを理解キタ――(゚∀゚)――!!

ドキュメント草案の案内通りにやってみる(設定編)

いずれ公式のここに載るはずです。
https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
だけど執筆時点ではまだ載ってないので、プルリクの草案をベースに実施します。

まずは本体のwebpack-bundle-analyzerをインストールします。

$npm install --save-dev webpack-bundle-analyzer

次に解析を実行するためのコマンドをpackage.jsonに追加します。
scriptsにstart,build,test,eject等あると思いますので、それと同列に追加です。

package.json
    "analyze": "webpack-bundle-analyzer build/bundle-stats.json",

これで事前準備は完了になります。簡単ですね。

ドキュメント草案の案内通りにやってみる(実行編)

あとはコマンドを実行して可視化してしまいましょう。

素人には途中のハイフンの意味が分からないですが、考えずに打ち込む(;^_^A

$npm run build -- --stats

実行が完了すると、buildフォルダにbundle-stats.jsonというファイルができています。
これが可視化のもとになるんでしょう。

$npm run analyze

実行するとブラウザが立ち上がって、パッケージサイズの割合が一目瞭然になります。

webpack解析.png

画像は私の小さなプロジェクトの結果ですが、導入パッケージが少ないのでGzippedでも約100KBのfirebase-firestore.jsのサイズが目につきます(;^_^A
プロジェクトが大きいといろんなパッケージが入り乱れていると思いますので、役割のわりに大きい場合は代替パッケージ等の検討になるでしょうか?
利用頻度が少なければコード分割して、利用時のみ動的ロードに変更するとかも。

まとめ

CreateReactAppを使ったReactアプリでも、今はwebpack-bundle-analyzerがすんなり使えちゃうという話でした。
公式ドキュメントが更新され、しばらく時間がたてばこの記事の役割は終えますが、それまでにどなたかの役に立てれば幸いです。
実行方法等が変更になる可能性がありますので、公式ドキュメントを最初に確認してください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactとAudioの付き合い方

Image from Gyazo

このプレイヤーの実装方法がお題です。

はじめに :smiley:

この記事は、React コンポーネントで内部状態をもつ要素を扱うための How を書いたものです。

今回のネタでいうと、内部状態をもつ要素とは、audio 要素のことです。

ここでいう内部状態をもつ要素はどんなものかというと、

const audio = new Audio("sample.mp3") // コンストラクタでaudio要素を生成

audio.play() // 再生
audio.currentTime = 50 // 経過時間を50秒にする

的なやつです。

内部状態を持っている要素は React の setState で更新する対象にできないし、しないべきです。
(↑ このテーマは 大きくて書ききれないので割愛します)

そうなると出てくる問題として、audio 要素の状態を直接更新しても、React コンポーネントは再描画されない んですよね。

はい。今回の問題は上記の通りで、 目指すのは、

audio 要素の状態を更新すると React コンポーネントが再描画される です。

そのために今回、render prop という React コンポーネントの実装パターンを使います。

render prop について :tophat:

render prop について今回は詳しく説明しませんが、以前書いた記事で使用例などを交えて説明しています。

簡単に説明すると 「コンポーネントの render メソッドを外部から定義するためのテクニック」 です。

本テーマ: 内部状態をもつ要素を React で扱うための How :writing_hand:


まず render prop パターンを使って何がしたいかというと、

audio 要素(内部状態をもつ厄介なやつ)の状態管理と画面描画の責務を分離したいんです。
audio 要素の状態を知ってるヤツには見た目の知識を与えず、その逆もまた然りとする感じです。

なので、実装としては、大きく二つのコンポーネントを用意します。

  • AudioProvider: audio の内部状態を知ってるし、変更できるけど、audio 要素が画面でどう使われるかは知らないコンポーネント

  • AudioPlayer: Provider から audio の情報(経過時間とか)を受け取って、画面を表示するコンポーネント

AudioPlayer (audio 情報を受け取って見た目を作るコンポーネント)

まず、audio 要素の 情報を受け取って画面表示する側の実装をお見せします

今回は冒頭の GIF でお見せした通り、再生・停止と 30 秒ジャンプができるだけのシンプルなプレイヤーを作りました。

AudioPlayer.jsx
const AudioPlayer = () => (
  <AudioProvider
    url="path/to/audioUrl"
    render={({ currentTime, paused, play, pause, jump }) => (
      <div>
        <p>currenttime: {currentTime}</p>

        <button onClick={paused ? play : pause}>
          {paused ? "Play" : "Pause"}
        </button>

        <button onClick={() => jump(30)}>30sec ▶︎</button>
      </div>
    )}
  />
);

render={({ currentTime, paused, play, pause, jump }) => ... の部分ですが、

render という名前の props に 引数を受け取ってコンポーネントを返す関数 を渡しています。

実際、このコンポーネントはそんな重要ではないです。 値や関数を受け取って画面に表示してるだけなので。

ただよくみると、受け取っている引数が currentTime(音声の経過時間) とか play(再生するための関数) とかですが、これは AudioProvider から渡されます。

それでは本題の AudioProvider 側の実装を 通して、 引数どっからきてるの? を 紹介します

AudioProvider (audio の内部状態を扱うコンポーネント)

class AudioProvider extends React.Component {
  audio = new Audio(this.props.url); // audio要素の生成

  componentDidMount = () => {
    /**
     * audioの内部状態に変化があったときに再描画するための処理
     * ex.) this.playメソッドが実行されると、"play" を登録してるイベントリスナーが反応し、
     *      コンポーネントが強制的に再描画される
     */
    this.audio.addEventListener("play", this.forceUpdate);
    this.audio.addEventListener("pause", this.forceUpdate);
    this.audio.addEventListener("ended", this.forceUpdate);
    this.audio.addEventListener("timeupdate", this.forceUpdate);
  };

  componentWillUnmount = () => {
    this.audio.removeEventListener("play", this.forceUpdate);
    this.audio.removeEventListener("pause", this.forceUpdate);
    this.audio.removeEventListener("ended", this.forceUpdate);
    this.audio.removeEventListener("timeupdate", this.forceUpdate);
  };

  // --------状態変更用のコールバック関数--------
  play = () => this.audio.play();
  pause = () => this.audio.pause();
  jump = value => (this.audio.currentTime += value);

  render = () =>
    this.props.render({
      currentTime: this.audio.currentTime,
      paused: this.audio.paused,
      play: this.play,
      pause: this.pause,
      jump: this.jump
    });
}

AudioProvider が

  • audio 要素を保持し、
  • 変更をゴリゴリ行い、
  • 変更を検知して自分自身(とその配下の AudioPlayer コンポーネント)を再描画し、
  • 新しい状態を render prop に渡す

などなど、汚れ仕事をたくさんしています。

ここで大事なのは、 audio要素自体をrender propに渡さない ということです。

audio 要素の変更がAudioProviderのおかげでブラックボックスにできているのに、
audio要素自体を渡してしまうと、渡された側(AudioPlayer 側)でaudio要素の状態を変えたりできちゃいます。

それを防ぐために、
audio 要素を分解して currentTime や paused などを ただの値として render prop に渡しているワケです。

  render = () =>
    this.props.render({
      currentTime: this.audio.currentTime,
      paused: this.audio.paused,
      play: this.play,
      pause: this.pause,
      jump: this.jump
    });

さいごに :airplane:

audio 要素だけではなくて、 内部状態をもつ要素をReactコンポーネントで綺麗に扱いたい 場合はこのパターンでだいたい乗り切れる気がします。

ただ、解決策は今回の render prop パターンだけではないと思うので、みなさんのプラクティスも、ぜひ教えて下さい

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む