react-redux-firebaseで、pushの自動生成key(ID)を事前に取得
firebaseでpushのkeyを事前取得
firebaseの公式サイトには、key(ID)の取得は以下のように書いてあります。
// Get a key for a new Post. var newPostKey = firebase.database().ref().child('posts').push().key;
react-redux-firebaseでも同様に、
const firebase = getFirebase(); const id = firebase.push('path/to/data').key
でkey(ID)を取得することができます。pushの第1引数にパスを指定して、第2引数は省略します。
firebase.push(‘path/to/data’)
はサーバへ通信を行わないので、オフライン時にも結果がすぐに返ってきます。
firebase.push('path/to/data').key
でkey(ID)を取得した後、firebase.set(`path/to/data/${id}`,obj)
を実行することで、先に取得したkey(ID)にデータを登録することができます。
keyの事前取得は、上の公式ページのように同一キーで複数のパスに書き込む場合や、以下で示すように新規作成されたデータがサーバにコミット(データが登録)されたかどうかを表示する場合に使用します。
使用例: リストに追加(新規作成)されたデータがサーバに送信中かを表示したい
タスク管理アプリにて、 pushしたデータの送信ステータスをデータごとに表示します。 リストにタスクを追加すると、送信中を示す矢印を表示し、コミット(サーバへのデータ登録)が完了すると矢印を消去します。エラーが起こった場合は、警告マークを表示します。
動作イメージ↓
action
actionの実装例です。
// reduxのaction export const addTodo = (uid, text) => { // uidはユーザID // redux-thunk return (dispatch, getState, {getFirebase}) => { const firebase = getFirebase(); // key(id)の取得 const id = firebase.push(`todos/${uid}`).key; // “送信中”の表示をする dispatch({type: ADD_TODO_REQUEST, todoId: id}); // 取得したidでデータをセットする firebase.set(`todos/${uid}/${id}`,{ completed: false, text, }).then(() => { // 表示をクリアする dispatch({type: ADD_TODO_SUCCESS, todoId: id}); }).catch(err => { // エラーを表示する dispatch({type: ADD_TODO_ERROR, todoId: id, err}); }); } }
reducer
reducerの実装例です。
const todoStatuses = (state = {}, action) => { switch (action.type) { case ADD_TODO_REQUEST: return { ...state, [action.todoId]: {status : 'sending', message: '送信中'}} case ADD_TODO_SUCCESS: return { ...state, [action.todoId]: {status : 'success'}} case ADD_TODO_ERROR: return { ...state, [action.todoId]: {status : 'error', message: 'エラーが発生しました。時間をおいて再度お試しください。'}} default: return state } } export default todoStatuses
あとは state.todoStatuses[todoId].status
の値によって表示を出し分ければ完了です。
あとがき
keyの取得方法はどうやらfirebaseのバージョンによって.key
か.key()
のどちらを使用するか異なるので、片方でうまくいかなかったらもう片方を試してみてもよいかと思います。
redux-thunkとreact-redux-firebaseのつなぎ方は、こちらの記事を参照ください。
バージョン
- react-redux-firebase: 2.1.8
Material-UIのwithStyleとreacrt-reduxのconnectを同時に使う
Material-UIのwithStyleとreacrt-reduxのconnect
react-reduxを利用しているアプリにMaterial-UIのstylesを組み込む、もしくはその逆を行うときに、どのようにすれば分からなかったので書いておきます。
connect
を使用したコンポーネント↓
import React from 'react' import { connect } from 'react-redux' const SampleComponent = () => { // 略 } const mapStateToProps = (state) => ({ // 略 }) const mapDispatchToProps = (dispatch) => ({ // 略 }) export default connect( mapStateToProps, mapDispatchToProps )(SampleComponent)
と
withStyles
を使用したコンポーネント↓
import React from 'react' import { withStyles } from '@material-ui/core/styles' const SampleComponent = () => { // 略 } const styles = theme => ({ // 略 }) export default withStyles(styles)(SampleComponent)
を、同時に使いたい。
composeを使う
reduxのcompose
関数を使用すると、withStyleとconnectを同時に使用することができます。
import React from 'react' import { compose } from 'redux' import { connect } from 'react-redux' import { withStyles } from '@material-ui/core/styles' const SampleComponent = () => { // 略 } const styles = theme => ({ // 略 }) const mapStateToProps = (state) => ({ // 略 }) const mapDispatchToProps = (dispatch) => ({ // 略 }) export default compose( withStyles(styles), connect( mapStateToProps, mapDispatchToProps ))(SampleComponent)
composeを使わない場合
compose関数は、関数を合成
してくれる関数です。合成
とは、”他の関数の戻り値”を入力とする関数を作成することです。
例えば、compose(a, b)(x)
は、a(b(x))
と同じ意味になります。
composeを使わない場合は以下のように書くことができます。
// 略 export default withStyles(styles)( connect( mapStateToProps, mapDispatchToProps )(SampleComponent) )
composeを使うよりネストが深くなりました。
バージョン
- react: 16.5.0
- react-redux: 5.0.7
- redux: 4.0.0
- @material-ui/core: 3.4.0
Firebaseアプリの公開 (STEP 3 : 他のユーザのタスクが見れるタスク管理アプリを作成する - React + Redux + Firebase チュートリアル)
★前回の記事
アプリが完成したので、Firebaseにアプリを公開します。
ビルドします。
$ yarn run build
一旦ローカルで正常に動くか確認しましょう。--only hosting
オプションで、hostingのみ動作するようにします。(functionはserveしない)
$ firebase serve --only hosting
http://localhost:5000にアクセスして、動作確認しましょう。 確認が終わったら、Firebaseにデプロイします。functionsは既にデプロイしてあるので、hostingのみデプロイします。
$ firebase deploy --only hosting === Deploying to 'todo-sample-xxxxx’… i deploying hosting i hosting[todo-sample-xxxxx]: beginning deploy... i hosting[todo-sample-xxxxx]: found 7 files in build ✔ hosting[todo-sample-xxxxx]: file upload complete i hosting[todo-sample-xxxxx]: finalizing version... ✔ hosting[todo-sample-xxxxx]: version finalized i hosting[todo-sample-xxxxx]: releasing new version... ✔ hosting[todo-sample-xxxxx]: release complete ✔ Deploy complete! Project Console: https://console.firebase.google.com/project/todo-sample-xxxxx/overview Hosting URL: https://todo-sample-xxxxx.firebaseapp.com
無事デプロイできたので、https://todo-sample-xxxxx.firebaseapp.com にアクセスします。(todo-sample-xxxxは自身のプロジェクトIDに置き換えてください。)
アプリが公開できました!
ログインしたユーザへのタスクの追加・更新と、ログインしたユーザとは別のユーザのタスクを見ることができました。
これでSTEP 3は終了です。本格的なアプリになってきましたね。STEP 4ではMateria-UIを導入して見た目を整えます。
★次回の記事
★目次
リファクタリング: 作成日時と更新日付の追加 (STEP 3 : 他のユーザのタスクが見れるタスク管理アプリを作成する - React + Redux + Firebase チュートリアル)
★前回の記事
Firebase Realtime Databaseでは、データの作成日時・更新日時は自動で付与されないので、クライアント側(javascript)で付与します。これに伴い、Cloud FunctionsもDatabaseの更新日時を使用するように変更します。データの作成日時・更新日時は、サンプルのアプリでは特に必要がないですが、アプリをリリースした後、問い合わせがあった場合の調査などに重要になります。
actionの更新
タスク作成時に、作成時間_createdAt
と_updatedAt
を付与し、更新時に_updatedAt
を更新します。
時刻関連の処理には、使いやすさのため、momentモジュールを使用します。
src/actions/todoActions.js
import moment from 'moment' // 追加 // 中略 export const addTodo = (uid, text) => { return (dispatch, getState, {getFirebase}) => { // 中略 const createdAt = moment().valueOf(); // 追加 firebase.push(`todos/${uid}`,{ completed: false, text, _createdAt : createdAt, // 追加 _updatedAt : createdAt // 追加 }) // 中略 } } export const toggleTodo = (uid, id) => { return (dispatch, getState, {getFirebase}) => { // 中略 const updatedAt = moment().valueOf(); // 追加 firebase.update(`todos/${uid}/${id}`, { completed: ! todo.completed, _updatedAt : updatedAt // 追加 }) // 中略 } }
database.rules.jsonの更新
_createdAt
と_updatedAt
を登録できるようにデータベースルールファイルを変更します。
database.rules.json
{ "rules": { "todos": { "$uid": { ".read": true, ".write": "$uid === auth.uid", "$todoId": { ".validate": "newData.hasChildren(['text', 'completed', '_createdAt', '_updatedAt'])", // 変更 "text": { ".validate": "newData.isString()" }, "completed": { ".validate": "newData.isBoolean()" }, "_createdAt": { // 追加 ".validate": "newData.isNumber()" }, "_updatedAt": { // 追加 ".validate": "newData.isNumber()" }, "$other": { ".validate": false } } } }, // 中略 } }
変更を反映します。
firebase deploy --only database
".validate": "newData.hasChildren(['text', 'completed', '_createdAt', '_updatedAt’])”
があるせいで、既存のタスクが更新できなくなってしまいました。今回は既存のタスクは削除します。次回からは初めから_createdAt
と_updatedAt
は付与しましょう。
Cloud Functionsの修正
タスクの作成トリガを/todos/{uid}/{todoId}/_createdAt
の作成、更新トリガを/todos/{uid}/{todoId}/_updatedAt
の更新とするように変更します。
また、recentUpdatedTodos
の_updatedAt
は、todos
の_updatedAt
から取得するように変更します。
fuctions/index.js
const addRecentUpdate = (uid, todoId, todo, eventType) => { // #1 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: todo.text, // #2 eventType, _updatedAt: todo._updatedAt // #3 })); }); } exports.addRecentUpdateOnCreate = functions.database.ref('/todos/{uid}/{todoId}/_createdAt') // #4 .onCreate((snapshot, context) => { const uid = context.params.uid; const todoId = context.params.todoId; return snapshot.ref.parent.once('value').then((snapshot) => { // #5 const todo = snapshot.val(); return addRecentUpdate(uid, todoId, todo, 'CREATE'); }); }) exports.addRecentUpdateOnUpdate = functions.database.ref('/todos/{uid}/{todoId}/_updatedAt') // #6 .onUpdate((change, context) => { const todoId = context.params.todoId; const uid = context.params.uid; return change.after.ref.parent.once('value').then((snapshot) => { // #7 const todo = snapshot.val(); return addRecentUpdate(uid, todoId, todo, 'UPDATE'); }); })
- addRecentUpdateの引数のうち、
text
をtodo
に変更します(#1)。 text
をtodo.text
で取得します(#2)。recentUpdatedTodos
の_updatedAt
を、todo
の_updatedAt
から取得します(#3)。- データパスを、
/todos/{uid}/{todoId}/text
から/todos/{uid}/{todoId}/_createdAt
に変更します(#4)。 - 親ノードを取得する処理を追加します(#5)。
- 関数名を、
addRecentUpdateOnUpdateCompleted
からaddRecentUpdateOnUpdate
に変更し、データパスを、/todos/{uid}/{todoId}/completed
から/todos/{uid}/{todoId}/_updatedAt
に変更します(#6)。 - textノードを取得する処理から、親ノードを取得する処理に変更します(#7)。
デプロイします。
$ 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 (56.34 KB) for uploading ✔ functions: functions folder uploaded successfully i functions: creating Node.js 6 function addRecentUpdateOnUpdate(us-central1)... i functions: updating Node.js 6 function addRecentUpdateOnCreate(us-central1)... i functions: updating Node.js 6 function limitRecentUpdatedTodos(us-central1)...
今回、functionの名前を変更したので、途中でaddRecentUpdateOnUpdateCompletedを削除するか聞かれます。”y”を選択します。
The following functions are found in your project but do not exist in your local source code: addRecentUpdateOnUpdateCompleted(us-central1) If you are renaming a function or changing its region, it is recommended that you create the new function first before deleting the old one to prevent event loss. For more info, visit https://firebase.google.com/docs/functions/manage-functions#modify ? Would you like to proceed with deletion? Selecting no will continue the rest o f the deployments. Yes i functions: deleting function addRecentUpdateOnUpdateCompleted(us-central1)... ✔ functions[addRecentUpdateOnUpdateCompleted(us-central1)]: Successful delete operation. ✔ functions[addRecentUpdateOnCreate(us-central1)]: Successful update operation. ✔ functions[limitRecentUpdatedTodos(us-central1)]: Successful update operation. ✔ functions[addRecentUpdateOnUpdate(us-central1)]: Successful create operation. ✔ Deploy complete! Project Console: https://console.firebase.google.com/project/todo-sample-xxxxx/overview
functionのデプロイが完了しました。
動作確認
動作確認をします。
タスクを新規登録します。
データベースを確認します。
_createdAt
と_updatedAt
が追加されています。
recentUpdatedTodos
の_updatedAt
はtodos
のものと同じ時刻です。
タスクを更新します。
データベースを確認します。
_updatedAt
が更新されました。recentUpdatedTodos
の_updatedAt
もtodos
の時刻で更新されています。
以上で作成日時と更新日付の追加の処理の完了です。Firebase Realtime Databaseを使用する際は、最初から全てのデータに作成日時と更新日時を付与することを強くおすすめします。
★次回の記事
★目次
リファクタリング : componentsとcontainersの階層化 (STEP 3 : 他のユーザのタスクが見れるタスク管理アプリを作成する - React + Redux + Firebase チュートリアル)
★前回の記事
componentのファイルが多くなり、管理しづらくなってきたので、画面パーツごとにディレクトリを分けます。
階層の移動と構成
以下に、src/components
以下のファイルの移動先を示します。
ファイル名 | 移動先 |
---|---|
App.js | App.js (変更なし) |
NoMatch.js | NoMatch.js (変更なし) |
Dashboard.js | dashboard/index.js |
RecentUpdatedTodos.js | dashboard/recentUpdatedTodos/index.js |
UserUpdatedTodo.js | dashboard/recentUpdatedTodos/UserUpdatedTodo.js |
Login.js | login/index.js |
Navbar.js | navbar/index.js |
Footer.js | todos/Footer.js |
Link.js | todos/Link.js |
NoticeForTodo.js | todos/Notice.js |
Todo.js | todos/Todo.js |
TodoList.js | todos/TodoList.js |
TodoComponent.js | todos/index.js |
同様にsrc/containers
も移動します。
ファイル名 | 移動先 |
---|---|
AddTodo.js | todos/AddTodo.js |
FilterLink.js | todos/FilterLink.js |
VisibleTodoList.js | todos/VisibleTodoList.js |
ソースコードの修正
階層を変更したので、ソースコード中のパスも書き換えます。
src/components/App.js
import Login from './login/' import Navbar from './navbar/' import Dashboard from './dashboard/' import TodoComponent from './todos/'
src/components/dashboard/index.js
import RecentUpdatedTodos from './recentUpdatedTodos/'
src/components/login/index.js
import { loginWithGoogle, logout } from '../../actions/authActions'
src/components/todos/Footer.js
import FilterLink from '../../containers/todos/FilterLink' import { VisibilityFilters } from '../../actions/visibilityFilterActions'
src/components/todos/index.js
// 前略 import { locationChangeOnTodos } from '../../actions/todoActions' import Footer from './Footer' import Notice from './Notice' import AddTodo from '../../containers/todos/AddTodo' import VisibleTodoList from '../../containers/todos/VisibleTodoList' class TodoComponent extends React.Component { // 中略 render() { // 中略 return ( <div> {isOwnTodos && <AddTodo uid={uid} />} <Notice /> {/* 変更 */} <VisibleTodoList uid={uid} isOwnTodos={isOwnTodos} /> <Footer /> </div> ) } }
src/containers/todos/AddTodo.js
import { addTodo } from '../../actions/todoActions'
src/containers/todos/FilterLink.js
import { setVisibilityFilter } from '../../actions/visibilityFilterActions' import Link from '../../components/todos/Link'
src/containers/todos/VisibleTodoList.js
import { toggleTodo } from '../../actions/todoActions' import TodoList from '../../components/todos/TodoList'
以上でcomponentsとcontainersの階層化ができました。1つのディレクトリのファイル数が10を超えたらディレクトリ分割を検討するとよいです。
★次回の記事
★目次
"最近の更新"を表示する (STEP 3 : 他のユーザのタスクが見れるタスク管理アプリを作成する - React + Redux + Firebase チュートリアル)
★前回の記事
/recentUpdatedTodos
をDashboardに表示します。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)。 /recentUpdatedTodos
をonce
で取得します(#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/ を表示します。
最近作成更新したタスクが、新しいものが上になるように表示されました。
ユーザ名をクリックします。
ユーザのタスク一覧に遷移します。
ログアウトします。
ログアウトしても最新の更新一覧が表示されます。
おめでとうございます!これでSTEP 3の全ての機能が実装できました!
★次回の記事
★目次
Cloud Functions for Firebaseを使用して、"最近の更新"の件数を制限する (STEP 3 : 他のユーザのタスクが見れるタスク管理アプリを作成する - React + Redux + Firebase チュートリアル)
★前回の記事
前回までで、/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
の子ノードを削除します。
タスクを作成・更新して、/recentUpdatedTodos
にデータを追加します。
Cloud Functionsのログを確認します。
/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
です。
_updateAtの古い順に①②③④と番号を振りました。
タスクを1件追加します。
追加後の/recentUpdatedTodos
です。
①と②のデータが消えて、件数が3件に制限されました。件数制限のスクリプトがうまく動いています。
保存件数の変更
今回は最新の10件をビューで表示するので、MAX_RECENT_UPDATED_TODOS
の値を”10”に変更します。
const MAX_RECENT_UPDATED_TODOS = 10;
デプロイします。
$ cd todo-sample $ firebase deploy --only functions
以上で、Cloud Functions for Firebaseを使用して、"最近の更新"の件数を制限することができました。
★次回の記事
★目次