10
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

FirebaseのonSnapshotを使ったときに起きた、serverTimestampがnullになる現象について

Last updated at Posted at 2020-12-18

React+TypeScriptでアプリを開発している時のこと。

「ユーザーがformに入力した内容をリアルタイムに画面にに反映したいな」と思い(チャットの様なものだと思ってください)、調べているとget()ではなく、onSnapshot()を使えばFirestore(db)に保存されたデータを即時画面に反映できると知り、使ってみた結果即時反映させることには成功したものの、なぜかserverTimestamp()がnullになるエラーに直面しました。

その時の対処法の解説になります。

コードの例

以下は上手く行った時のReactのコード例ですが、他のフレームワークやライブラリでも同じ様な感じだと思います。

CommentForm.tsx
  useEffect(() => {
    let comments: any = [];
    const unsubscribe: any = db
      .collection("posts")
      .doc(id)
      .collection("comments")
      .orderBy("createdAt", "desc")
      .onSnapshot((snapshots) => {
        snapshots.docChanges().forEach((change) => {
          const data = change.doc.data({ serverTimestamps: "estimate" });
          const changeType = change.type;
          const date = data.createdAt.toDate();

          switch (changeType) {
            case "added":
              comments.push({ ...data, createdAt: date });
              break;
            case "modified":
              const index = comments.findIndex(
                (comment: any) => comment.id === change.doc.id
              );
              comments[index] = comment;
              break;
            case "removed":
              comments = comments.filter(
                (comment: any) => comment.id !== change.doc.id
              );
              break;
            default:
              break;
          }
        });
        setComment(comments);
        unsubscribe();
      });
  }, []);

解決した方法

解決した方法はかなり簡単でした。。。

doc.data({ serverTimestamps: "estimate" }).createdAt

解説

FirebaseでgetやonSnapshotを使ってデータを呼び出した際に、

snapshots.docs.forEach(doc => {...})

の様な感じで、データを取り出すと思います。

この後に、doc.data()とすると一つ一つのデータのオブジェクトが取得できます。

しかし、

doc.data().createdAt

とすると、最新(追加した瞬間)のデータがnullになってしまうのですが、

doc.data({ serverTimestamps: "estimate" }).createdAt

とすることで、追加した瞬間のnullの状態のtimestampに対して、timestampを推定して、確定するまでとりあえず仮のTimestampを入れておいてくれます。

Firebaseの公式ドキュメントを見ていただけたらわかると思いますが、このdoc.data()data()に関して以下の様な解説がされています。

ドキュメント内のすべてのフィールドをオブジェクトとして取得します。文書が存在しない場合は 'undefined' を返します。デフォルトでは、まだ最終的な値に設定されていない FieldValue.serverTimestamp() の値は null として返されます。これをオーバーライドするには、オプションオブジェクトを渡す必要があります。

今回のエラーも加味して簡単に言い換えると、、、

data()でオプションを何も指定していない場合は、FieldValue.serverTimestamp()最終的な値を決定するまでnullを返します。nullが返されたくないなら、オプションを指定すればなんとかできます。

といったところでしょうか。

ドキュメントを見てみると、data()のオプションは2つある様です。

オプション 説明
estimate 保留中のサーバータイムスタンプはローカルクロックに基づいた推定値を返します。この推定値は最終的な値とは異なり、サーバーの結果が利用可能になると、これらの値が変更されます。
previous 保留中のタイムスタンプは無視され、代わりに以前の値を返します。
none 省略された場合や 'none' に設定された場合は、サーバの値が利用可能になるまでの間、デフォルトで null が返されます。

こんなところに大ヒントが書いてありましたね😅

答えは公式ドキュメントに📃

Firebaseの公式ドキュメントはやはりかなり充実していて、大抵のfirebase関連の問題はドキュメントに書いてあると思いました😅

今後はFirebaseを使うときはわからなかったらドキュメントをしっかり読む、という習慣を身につけようと思いました。

参考

公式ドキュメント

公式ドキュメントのdataのページ

公式ドキュメントのdataのoptionのページ

10
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?