yucatio@システムエンジニア

趣味で作ったものいろいろ

"最近の更新"を表示する (STEP 3 : 他のユーザのタスクが見れるタスク管理アプリを作成する - React + Redux + Firebase チュートリアル)

★前回の記事

yucatio.hatenablog.com

/recentUpdatedTodosDashboardに表示します。Dashboardの下にRecentUpdatedTodosコンポーネントを新規作成します。

moment.jsモジュールのインストール

各タスクの更新時間を表示は、「○分前」「○時間前」など、現在からの経過時間を概算で表示します。これにはmoment.jsを利用するので、インストールします。

yarn add moment

"最近の更新”の表示

Dashboardの下にRecentUpdatedTodosコンポーネントを新規作成し、さらにその下にUserUpdatedTodoコンポーネントを作成します。

src/components/Dashboard.js

// 前略
import RecentUpdatedTodos from './RecentUpdatedTodos'  // 追加
 
const Dashboard = () => (
  <div>
    <RecentUpdatedTodos />
  </div>
)

// 後略

src/components/RecentUpdatedTodos.js (新規作成) (実行前にコメントは削除してください)

import React from 'react'
import { compose } from 'redux'
import { connect } from 'react-redux'
import { firebaseConnect, isEmpty, isLoaded } from 'react-redux-firebase'
import PropTypes from 'prop-types'
import UserUpdatedTodos from './UserUpdatedTodo'

const RecentUpdatedList = (todos) => {
  if (!isLoaded(todos)) {
    return <div>読み込み中…</div>
  }
  if (isEmpty(todos)) {
    return <div>データがありません。</div>
  }
  return (
    <ul>
      {todos.map(({key, value:todo}) =>  // #4
        <UserUpdatedTodos  key={key} {...todo}/>
      )}
    </ul>
  )
}

let RecentUpdatedTodos = ({todos}) => {
  return (
    <div>
      <h1>最近の更新</h1>
      {RecentUpdatedList(todos)}
    </div>
  )
}
RecentUpdatedTodos.propTypes = {
  todos: PropTypes.arrayOf(
    PropTypes.shape({
      key: PropTypes.string.isRequired,
      value: PropTypes.object.isRequired,
    })
  ),
}

const firebaseQueries = ({uid}) => (
  [
    {path: `recentUpdatedTodos`, type: 'once', queryParams: [ 'orderByChild=_updatedAt', 'limitToLast=10' ]}  // #1
  ]
)

const mapStateToProps = ({firebase: {ordered : {recentUpdatedTodos}}}) => {  // #2
  return {
    todos: recentUpdatedTodos && recentUpdatedTodos.reverse()  // #3
  }
}

RecentUpdatedTodos = compose(
  firebaseConnect(firebaseQueries),
  connect(
   mapStateToProps
))(RecentUpdatedTodos)

export default RecentUpdatedTodos;
  • 並び替えられたデータはfirebase.orderedで取得できます(#2)。
  • /recentUpdatedTodosonceで取得します(#1)。_updatedAtの昇順で並び替え、最後の10件を取得します。
  • Viewで降順で表示するため、データが存在する場合はreverseして逆順に並び替えます(#3)。
  • 並び替えられたデータは、{key: recentUpdatedTodosのキー, value: recentUpdatedTodosの値}というオブジェクトの配列になっているので、todos.mapで各ノードのキーと値が入ったオブジェクトを取得します(#4)。

src/components/UserUpdatedTodo.js (新規作成)

import React from 'react'
import { Link } from 'react-router-dom'
import moment from 'moment'
import 'moment/locale/ja'  // #1
import PropTypes from 'prop-types'

const UserUpdatedTodo = ({text, eventType, uid, displayName, _updatedAt}) => (
  <li>
    <Link to={`/users/${uid}/todos`}>{displayName}</Link>さんが {text} を{ eventType === 'CREATE' ? '作成' : '更新'}  {/* #2 */}
    しました。 ({ moment(_updatedAt).fromNow()})  {/* #3 */}
  </li>
)

UserUpdatedTodo.propTypes = {
  text: PropTypes.string.isRequired,
  eventType: PropTypes.string.isRequired,
  uid: PropTypes.string.isRequired,
  displayName: PropTypes.string.isRequired,
  _updatedAt: PropTypes.number.isRequired
}

export default UserUpdatedTodo;
  • import 'moment/locale/ja’でmoment.jsの日本語化をします(#1)。
  • displayNameに、そのユーザのタスク一覧へのリンクを貼ります(#2)。
  • moment#fromNow()で経過時間を概算を取得します(#3)。

preserveOnLogoutへの設定追加

react-redux-firebaseのpreserveOnLogoutの設定も忘れずに行います。

src/index.js

const createStoreWithFirebase = compose(
  applyMiddleware(thunk.withExtraArgument({getFirebase})),
  reactReduxFirebase(firebase, {userProfile: 'users', preserveOnLogout: ['todos', 'users', 'recentUpdatedTodos']})  // 変更
)(createStore);

動作確認

http://localhost:3000/ を表示します。

f:id:yucatio:20181028225616p:plain

最近作成更新したタスクが、新しいものが上になるように表示されました。

ユーザ名をクリックします。

f:id:yucatio:20181029134921p:plain

ユーザのタスク一覧に遷移します。

ログアウトします。

f:id:yucatio:20181029140124p:plain

ログアウトしても最新の更新一覧が表示されます。

おめでとうございます!これでSTEP 3の全ての機能が実装できました!

★次回の記事

yucatio.hatenablog.com

★目次

yucatio.hatenablog.com

Cloud Functions for Firebaseを使用して、"最近の更新"の件数を制限する (STEP 3 : 他のユーザのタスクが見れるタスク管理アプリを作成する - React + Redux + Firebase チュートリアル)

★前回の記事

yucatio.hatenablog.com

前回までで、/recentUpdatedTodosへのデータの追加を行うことはできましたが、これではどんどんデータが溜まって行ってしまうので、個数を制限します。制限を超えたら古いデータを削除します。

firebaseのサンプル(limit-children)があるので、このコードをもとにします。

実装方針

/recentUpdatedTodosに新しくデータが登録された時に、古いデータを削除します。何件のデータを残すかは、MAX_RECENT_UPDATED_TODOSで制御します。 動作確認を簡単にするため、MAX_RECENT_UPDATED_TODOSは一旦”3”にします。

“/recentUpdatedTodos”の子ノードの数の取得

はじめに、/recentUpdatedTodosの子ノードの数の取得をするコードを書きます。削除処理が必要なのは子の数がMAX_RECENT_UPDATED_TODOSより大きい場合なので、それ以下の場合は早期にreturnします。

functions/index.js

const MAX_RECENT_UPDATED_TODOS = 3;

exports.limitRecentUpdatedTodos = functions.database.ref('/recentUpdatedTodos/{todoId}/_updatedAt')  // #1
  .onCreate((snapshot, context) => {
    const recentUpdatedTodosRef = snapshot.ref.parent.parent;  // #2
    return recentUpdatedTodosRef.once('value').then((todosSnapshot) => {  // #3

      console.log('numChildren: ' + todosSnapshot.numChildren());  // #4

      if (todosSnapshot.numChildren() <= MAX_RECENT_UPDATED_TODOS) {
        console.log('Less than or equals to ' + MAX_RECENT_UPDATED_TODOS);
        return null;
      }

      console.log('More than ' + MAX_RECENT_UPDATED_TODOS);
      return null;
    });
})
  • データ追加のトリガを/recentUpdatedTodos/{todoId}/_updatedAtに設定します(#1)。
  • /recentUpdatedTodosは、_updatedAtから見て親の親なので、/recentUpdatedTodosのリファレンスはsnapshot.ref.parent.parentで取得することができます(#2)。
  • /recentUpdatedTodosのデータを取得します(#3)。
  • 子ノードの個数はnumChildren()で取得します(#4)。

子ノード数取得の動作確認

デプロイして動作確認します。

$ cd todo-sample
$ firebase deploy --only functions

動作確認のため、一旦DBの/recentUpdatedTodosの子ノードを削除します。

f:id:yucatio:20181026225707p:plain

タスクを作成・更新して、/recentUpdatedTodosにデータを追加します。

f:id:yucatio:20181027214107p:plain

Cloud Functionsのログを確認します。

f:id:yucatio:20181027215027p:plain

/recentUpdatedTodos```の子ノードの数と、MAX_RECENT_UPDATED_TODOS``より大きいか小さいかが表示されました。

制限件数を超えたら、古いデータを削除する

制限件数を超えたら、古いデータから削除するように修正します。データを_updatedAtで並び替え、最初の方のデータから削除します。

functions/index.js

const MAX_RECENT_UPDATED_TODOS = 3;

exports.limitRecentUpdatedTodos = functions.database.ref('/recentUpdatedTodos/{todoId}/_updatedAt')
  .onCreate((snapshot, context) => {
    const recentUpdatedTodosRef = snapshot.ref.parent.parent;
    return recentUpdatedTodosRef.orderByChild('_updatedAt').once('value').then((todosSnapshot) => { // #1

      if (todosSnapshot.numChildren() <= MAX_RECENT_UPDATED_TODOS) {
        return null;
      }

      let childCount = 0;
      const updates = {};
      todosSnapshot.forEach((child) => {
        // remove old updates
        if (++childCount <= todosSnapshot.numChildren() - MAX_RECENT_UPDATED_TODOS) {  // #2
          updates[child.key] = null;  // #3
        }
      });
      return recentUpdatedTodosRef.update(updates);  // #4
    });
})
  • 最近の更新のデータを最終更新時間順に並び変えるため、orderByChild('_updatedAt’を追加します(#1)。データは昇順に並びます。
  • 子ノードの個数 - MAX_RECENT_UPDATED_TODOSぶん、/recentUpdatedTodosの子ノードを削除します(#2)。
  • 削除したいデータの値をnullにします(#3)。
  • 更新内容を、updateの引数として渡します(#4)。

件数の制限の動作確認

デプロイして動作確認します。

$ cd todo-sample
$ firebase deploy --only functions

実行前の/recentUpdatedTodosです。

f:id:yucatio:20181027220309p:plain

_updateAtの古い順に①②③④と番号を振りました。

タスクを1件追加します。

f:id:yucatio:20181027220709p:plain:w200

追加後の/recentUpdatedTodosです。

f:id:yucatio:20181027221319p:plain

①と②のデータが消えて、件数が3件に制限されました。件数制限のスクリプトがうまく動いています。

保存件数の変更

今回は最新の10件をビューで表示するので、MAX_RECENT_UPDATED_TODOSの値を”10”に変更します。

const MAX_RECENT_UPDATED_TODOS = 10;

デプロイします。

$ cd todo-sample
$ firebase deploy --only functions

以上で、Cloud Functions for Firebaseを使用して、"最近の更新"の件数を制限することができました。

★次回の記事

yucatio.hatenablog.com

★目次

yucatio.hatenablog.com

Cloud Functions for Firebaseを使用して、タスク更新時に"最近の更新"へデータを追加する (STEP 3 : 他のユーザのタスクが見れるタスク管理アプリを作成する - React + Redux + Firebase チュートリアル)

★前回の記事

yucatio.hatenablog.com

引き続き、addRecentUpdateOnUpdateCompletedを実装します。タスクが更新された時に、/recentUpdatedTodosにデータを追加します。

タスク更新時に"最近の更新"へデータを追加する

addRecentUpdateOnUpdateCompletedの処理は、addRecentUpdateOnCreateとほぼ同様です。textの取得処理を追加します。

functions/index.js

exports.addRecentUpdateOnUpdateCompleted = functions.database.ref('/todos/{uid}/{todoId}/completed')
  .onUpdate((change, context) => {
    const todoId = context.params.todoId;
    const uid = context.params.uid;
    let text;

    return change.after.ref.parent.child('text').once('value').then((snapshot) => {  // #1
      text = snapshot.val();
      return admin.database().ref('/users/' + uid + '/displayName').once('value');
    }).then((snapshot) => {
      const displayName = snapshot.val();
      return (admin.database().ref('/recentUpdatedTodos/' + todoId).set({
        uid,
        displayName,
        text,
        eventType: 'UPDATE',
        _updatedAt: admin.database.ServerValue.TIMESTAMP
      }));
    });
})
  • change.after.refで更新後の値へのreferenceを取得します。parentで親ノードを取得し、さらにchild('text')でtextノードを取得します(#1)。

動作確認

デプロイして動作確認します。

$ cd todo-sample
$ firebase deploy --only functions

タスクの完了フラグを切り替えます。

f:id:yucatio:20181024222007p:plain:w200

データベースのデータを確認します。

f:id:yucatio:20181024222309p:plain

/recentUpdatedTodosにtodoIdをキーとして、uidとdisplayName、text、eventType('UPDATE')、更新時間(_updatedAt)が追加されました。

リファクタリング

/recentUpdatedTodosへの書き込みは、タスクの作成・更新で共通なので、共通部分を関数として切り出します。

functions/index.js

const addRecentUpdate = (uid, todoId, text, eventType) => {
  return admin.database().ref('/users/' + uid + '/displayName').once('value').then((snapshot) => {
    const displayName = snapshot.val();
    return (admin.database().ref('/recentUpdatedTodos/' + todoId).set({
      uid,
      displayName,
      text,
      eventType,
      _updatedAt: admin.database.ServerValue.TIMESTAMP
    }));
  });
}

exports.addRecentUpdateOnCreate = functions.database.ref('/todos/{uid}/{todoId}/text')
  .onCreate((snapshot, context) => {
    const uid = context.params.uid;
    const todoId = context.params.todoId;
    const text =  snapshot.val();

    return addRecentUpdate(uid, todoId, text, 'CREATE');
})

exports.addRecentUpdateOnUpdateCompleted = functions.database.ref('/todos/{uid}/{todoId}/completed')
  .onUpdate((change, context) => {
    const todoId = context.params.todoId;
    const uid = context.params.uid;

    return change.after.ref.parent.child('text').once('value').then((snapshot) => {
      const text = snapshot.val();
      return addRecentUpdate(uid, todoId, text, 'UPDATE');
    });
})

動作確認

デプロイして動作確認します。

$ cd todo-sample
$ firebase deploy --only functions

データを作成・更新してデータを確認します。

f:id:yucatio:20181024223030p:plain

タスクの作成・更新ともに/recentUpdatedTodosへ正常にデータが作成されました。

以上でタスク作成・更新時に"最近の更新"へデータを追加する処理が完成しました。

★次回の記事

yucatio.hatenablog.com

★目次

yucatio.hatenablog.com

Cloud Functions for Firebaseを使用して、タスク作成時に"最近の更新"へデータを追加する (STEP 3 : 他のユーザのタスクが見れるタスク管理アプリを作成する - React + Redux + Firebase チュートリアル)

★前回の記事

yucatio.hatenablog.com

addRecentUpdateOnCreateを実装します。タスクが作成された時に、/recentUpdatedTodosにデータを追加します。

表示したいデータ

“最近の更新”は、以下のようになります。ユーザ名をクリックすると、ユーザのタスク一覧へ遷移します。

f:id:yucatio:20181011124946p:plain

これを表示するのに以下のデータが必要になります。

  • uid (ユーザID)
  • displayName (ユーザ名)
  • todo.text (タスク名)
  • 作成か更新か
  • 更新時間

/recentUpdatedTodosの下にtodoIdをキーとしてデータを作成するので、

  • todoId

のデータも必要です。/recentUpdatedTodosの下にtodoIdをキーとしてデータを作成することによって、1つのタスクが短時間に何度も更新されても、画面への表示は最後の1回だけになります。また、べき等性も担保されます。

必要なデータについては、

  • uidとtodoIdについてはパスパラメータの値を使用します。
  • displayNameは/users/{uid}/displayNameから取得します。
  • todo.textは作成についてはそのまま値を取得することができますが、更新の場合は一旦親ノードを取得してからtextの値を取得する必要があります。
  • 作成か更新かについては、eventTypeというプロパティを用意し、CREATEまたはUPDATEをセットするようにします。
  • 更新時間はadmin.database.ServerValue.TIMESTAMPで取得します。

タスク作成時に"最近の更新"へデータを追加する(displayName以外)

最初に、displayName以外の、

  • uid
  • todo.text
  • 作成か更新か
  • 更新時間

をtodoIdをキーとして/recentUpdatedTodosに登録します。

functions/index.js

exports.addRecentUpdateOnCreate = functions.database.ref('/todos/{uid}/{todoId}/text')
  .onCreate((snapshot, context) => {
    const uid = context.params.uid;
    const todoId = context.params.todoId;
    const text = snapshot.val();  // #1

    return admin.database().ref('/recentUpdatedTodos/' + todoId).set({  // #2
      uid,
      text,
      eventType: 'CREATE',
      _updatedAt: admin.database.ServerValue.TIMESTAMP
    });
})
  • 指定したpathの値は、snapshot.val()で取得できます(#1)。
  • /recentUpdatedTodos/{todoId}にデータをセットします(#2)。

動作確認(displayName以外)

デプロイします。

$ cd todo-sample
$ firebase deploy --only functions

タスクを追加します。

f:id:yucatio:20181024112600p:plain:w200

データベースのデータを確認します。

f:id:yucatio:20181024113107p:plain

/recentUpdatedTodosにtodoIdをキーとして、uidとtext、eventType('CREATE')、更新時間(_updatedAt)が追加されました。

データが追加されない場合は、 Firebase console > Functions > ログで何かエラーが出ていないか確認してください。

タスク作成時に"最近の更新"へデータを追加する(displayNameを含める)

displayNameを追加します。displayNameは、/users/{uid}/displayNameで取得します。取得処理は非同期になります。

functions/index.js

exports.addRecentUpdateOnCreate = functions.database.ref('/todos/{uid}/{todoId}/text')
  .onCreate((snapshot, context) => {
    const uid = context.params.uid;
    const todoId = context.params.todoId;
    const text = snapshot.val();

    return admin.database().ref('/users/' + uid + '/displayName').once('value').then((snapshot) => {  // #1
      const displayName = snapshot.val();
      return admin.database().ref('/recentUpdatedTodos/' + todoId).set({
        uid,
        displayName,
        text,
        eventType: 'CREATE',
        _updatedAt: admin.database.ServerValue.TIMESTAMP
      });
    });
})
  • /users/{uid}/displayNameonce()で取得します(#1)。

動作確認(displayNameを含める)

デプロイして動作確認します。

$ cd todo-sample
$ firebase deploy --only functions

タスクを追加します。

f:id:yucatio:20181024115013p:plain:w200

データベースのデータを確認します。

f:id:yucatio:20181024115550p:plain /recentUpdatedTodosにtodoIdをキーとして、uidとdisplayName、text、eventType('CREATE')、更新時間(_updatedAt)が追加されました。

以上でタスク作成時に"最近の更新"へデータを追加することができました。

★次回の記事

yucatio.hatenablog.com

★目次

yucatio.hatenablog.com

Cloud Functions for Firebase用の最初のコードの記述とデプロイ (STEP 3 : 他のユーザのタスクが見れるタスク管理アプリを作成する - React + Redux + Firebase チュートリアル)

★前回の記事

yucatio.hatenablog.com

Cloud Functions for Firebaseで最初に小さいコードを書いて動作確認をします。 手始めに、DBの/todos/{uid}の下にタスクが追加された時と、タスクが更新された時にコンソールへメッセージを表示します。

トリガの動作確認用コードの作成

/todos/{uid}/の下にタスクが追加された時と、タスクが更新された時にコンソールにメッセージを表示します。

functions/index.js

const functions = require('firebase-functions');  // #1
const admin = require('firebase-admin');  // #2
admin.initializeApp();  // #3

exports.addRecentUpdateOnCreate = functions.database.ref('/todos/{uid}/{todoId}/text')  // #4
  .onCreate((snapshot, context) => {
    const uid = context.params.uid;  // #5
    const todoId = context.params.todoId;  // #6

    console.log('addRecentUpdateOnCreate called. uid=' + uid + ', todoId=' + todoId);  // #7

    return null;  // #8
})

exports.addRecentUpdateOnUpdateCompleted = functions.database.ref('/todos/{uid}/{todoId}/completed')  // #9
  .onUpdate((change, context) => {
    const todoId = context.params.todoId;
    const uid = context.params.uid;

    console.log('addRecentUpdateOnUpdateCompleted called. uid=' + uid + ', todoId=' + todoId);

    return null;
})
  • firebase-functionsを使用します(#1)。このモジュールはCloud Functionを使用するのに必要です。
  • firebase-adminを使用します(#2)。このモジュールはFirebase Realtime Databaseを利用するのに必要です。
  • admin.initializeAppでadminを初期化します(#3)。
  • タスクが追加された時に実行される関数を定義します(#4)。functions.database.ref('/todos/{uid}/{todoId}/text’).onCreate()/todos/{uid}/{todoId}/text’が新規作成された時に、onCreateの引数で与えた関数を実行します。パスは、/todos/{uid}/{todoId}でなく、/todos/{uid}/{todoId}/textです。なるべく深い所にトリガを設定します。
  • pathで波括弧で囲った部分はパラメータとして扱われ、context.params.{パラメータ名}で取得することができます(#5, #6)。
  • console.logで、Firebaseのコンソールにログを出力することが可能になります(#7)。
  • 関数はPromiseか値を返す必要があります。特に返す値がない場合はnullでよいでしょう(#8)。処理が完了した時に何も返さないとエラーになります。
  • タスクが更新された時に実行される関数を定義します(#9)。今回はcompletedの値しか変化しないので、pathは/todos/{uid}/{todoId}/completedです。

Cloud Functionsのデプロイ

functions/index.jsをデプロイします。

$ cd todo-sample
$ firebase deploy --only functions

=== Deploying to 'todo-sample-xxxxx’…

i  deploying functions
Running command: npm --prefix "$RESOURCE_DIR" run lint

> functions@ lint /Users/yuka/react/todo-sample/functions
> eslint .

✔  functions: Finished running predeploy script.
i  functions: ensuring necessary APIs are enabled...
✔  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (55.94 KB) for uploading
✔  functions: functions folder uploaded successfully
i  functions: creating Node.js 6 function addRecentUpdateOnCreate(us-central1)...
i  functions: creating Node.js 6 function exports.addRecentUpdateOnUpdateCompleted(us-central1)...
✔  functions[addRecentUpdateOnCreate(us-central1)]: Successful create operation. 
✔  functions[exports.addRecentUpdateOnUpdateCompleted(us-central1)]: Successful create operation. 

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/todo-sample-xxxxx/overview

デプロイが完了しました。

Firebaseコンソールでデプロイした関数を確認します。

f:id:yucatio:20181023132227p:plain

動作確認

タスクを追加します。

f:id:yucatio:20181023132805p:plain:w200

コンソールを確認します。

f:id:yucatio:20181023133600p:plain

addRecentUpdateOnCreate called.と、uidとtodoIdが表示されていることが確認できました。

タスクを更新します。

f:id:yucatio:20181023134415p:plain:w200

コンソールを確認します。

f:id:yucatio:20181023133909p:plain

addRecentUpdateOnUpdateCompleted called.と、uidとtodoIdが表示されていることが確認できました。

以上で、/todos/{uid}の下にタスクが追加された時と、タスクが更新された時にコンソールへメッセージを表示することができました。

作成トリガと更新トリガは、更新日時を使用するのがおすすめ

今回は、タスクの作成トリガは/todos/{uid}/{todoId}/textの作成、更新トリガは/todos/{uid}/{todoId}/completedの更新としました。問題なく動きますが、将来的に項目が増えた時にトリガを追加し忘れる恐れがあります。 また、データごとにトリガになるプロパティが違うのは面倒です。

この問題を解決するには、すべてのデータに、作成日付を表す_createdAtプロパティと更新日付を表す_updatedAtプロパティを付与します。データの作成は、_cretedAtの作成をトリガにし、データの更新は、_updatedAtの更新をトリガにします。

以下の記事でこれの対応をしています。

yucatio.hatenablog.com

★次回の記事

yucatio.hatenablog.com

★目次

yucatio.hatenablog.com

Cloud Functions for Firebaseの導入とfirebase init functionsの実行 (STEP 3 : 他のユーザのタスクが見れるタスク管理アプリを作成する - React + Redux + Firebase チュートリアル)

★前回の記事

yucatio.hatenablog.com

アプリにCloud Functions for Firebaseを導入します。

Cloud Functions for Firebaseでできること

Cloud Functions for Firebaseでは、ユーザがログインしたり、データベースに書き込まれた際に関数を実行できます。関数内では、以下の処理などを行うことができます。

  • DBへの書き込み
  • ユーザへのプッシュ通知
  • 外部API連携

今回は、タスクが作成・更新された時に、DBに書き込みを行います。

firebase init functionsの実行

Cloud Functionsを使用するために、firebase init functionsを実行して、Cloud Functionsに必要なファイルを作成します。

# プロジェクトのルートディレクトリに移動
$ sh todo-sample

$ firebase init functions

firebase initの時と同様、また燃えているFirebaseの文字が表示されます。

f:id:yucatio:20181021232602p:plain

“既存のFirebaseプロジェクトディレクトリを初期化しようとしていることに気をつけてください。”とのメッセージが表示されます。

f:id:yucatio:20181021232647p:plain

Project Setupはすでに完了しているのでスキップされます。

f:id:yucatio:20181021232726p:plain

Function Setupでは、使用する言語が選べます。このチュートリアルでは、JavaScriptを使用します。

f:id:yucatio:20181021232824p:plain

続いて、ESLintを使用するかどうか聞かれますので、”y”を選択します。

f:id:yucatio:20181021232850p:plain

npmで依存するパッケージを自動でインストールするか聞かれますので、”Y”を入力します。

f:id:yucatio:20181021232920p:plain

依存パッケージがインストールされ、設定が完了しました。

f:id:yucatio:20181021233507p:plain

ディレクトリ構成は以下のようになります。

todo-sample/
|_ functions/      # 新規ディレクトリ
   |_ .eslintrc.json
   |_ index.js
   |_ node_modules/
   |  |_ ...modules...
   |_ package-lock.json
   |_ package.json

firebase.jsonに以下の設定が追加されています。

{
  // 略
  "functions": {
    "predeploy": [
      "npm --prefix \"$RESOURCE_DIR\" run lint"
    ]
  }
}

以上でCloud Functions for Firebaseの開発を行う準備ができました。

★次回の記事

yucatio.hatenablog.com

★目次

yucatio.hatenablog.com

react-redux-firebase使用時、ログアウト時にデータが消えないようにする (STEP 3 : 他のユーザのタスクが見れるタスク管理アプリを作成する - React + Redux + Firebase チュートリアル)

★前回の記事

yucatio.hatenablog.com

他のユーザのタスクは閲覧できて、自身のタスクは閲覧・編集できるようになったアプリに発生した2つのバグのうち、もう1つのバグを修正します。

バグ再現手順

  1. ログインします
  2. 自身のタスク一覧が表示されることを確認します。
  3. ログアウトします。
  4. “タスク一覧を読み込み中…” がずっと表示され、タスク一覧が表示されません。

f:id:yucatio:20181021153110p:plain

バグの原因

react-redux-firebaseではログアウト時にstate.firebase.datastate.firebase.authの内容を全てクリアします。そのため、todosの内容がundefinedになり、"読み込み中"がずっと表示されます。

修正方針

react-redux-firebaseに preserveOnLogout を設定します。 この設定をすると、ログアウト時にもデータが消えません。

コーディング

preserveOnLogoutを、reactReduxFirebaseに設定します。todosの内容とusersの内容をログアウト時にも表示したいので、この2つを設定します。

src/index.js

const createStoreWithFirebase = compose(
  applyMiddleware(thunk.withExtraArgument({getFirebase}),
  reactReduxFirebase(firebase, {userProfile: 'users', preserveOnLogout: ['todos', 'users']})  // 変更
)(createStore);

動作確認

  1. ログインします
  2. タスク一覧が表示されることを確認します。
  3. ログアウトします。
  4. タスク一覧が引き続き表示されることを確認します。

f:id:yucatio:20181021153526p:plain

以上でログアウト時に”読み込み中”がずっと表示されるバグが修正できました。 こちらのバグも見つけにくいものでした。ログアウト時のテストもテストケースに含めておくとよいですね。

★次回の記事

yucatio.hatenablog.com

★目次

yucatio.hatenablog.com