LoginSignup
2
0

More than 1 year has passed since last update.

Firestoreでいいね機能を実装してみる

Last updated at Posted at 2023-03-09

学ぶ

これを読めばFirestoreでのリレーションもどきについてはおおむね理解できるが、RDBが恋しくてたまらない。

出来上がったカスタムhook

いいね状態を判定するためのRefを監視して、トグルするようにしている。

incrementメソッドを使用する場合、データを監視しているSubscribeが保留中の書き込みを反映しない設定にしている(snapshot.metadata.hasPendingWritesを弾いたり)と何故か更新を受け取れないようで、この設定を切る必要があった。なぜかはよくわからない。

また、拡張機能の分散カウンタも試してみたが、batch処理に絡められないのと、エミュレーター上で動かすと権限エラーで使用できず苦しんだ。
かなりの数のリクエストを送ってみてもincrementで処理できていたためコストにリターンが見合っていないと考え、今回は使用しなかった。

likePost.ts
import { useFirestoreWriteBatch } from '@react-query-firebase/firestore';
import { doc, increment, writeBatch } from 'firebase/firestore';

import { db } from '@/config/firebase';
import { useFirestore } from '@/hooks/useFirestore';
import { useAuthContext } from '@/lib/auth';
import { timestampTemp } from '@/utils/constants';

import type { Post } from '../types';

export type LikePostDTO = {
  data: Post;
};

type UseLikePostOptions = LikePostDTO & {
  config?: {
    // ...
  };
};

export const useLikePost = ({ data }: UseLikePostOptions) => {
  const auth = useAuthContext();

  const batch = writeBatch(db);
  const LikePostBatch = useFirestoreWriteBatch(batch);

  const postRef = doc(db, 'users', data.author.path.split('/')[1], 'posts', data.id);
  const userRef = doc(db, 'users', auth?.user ? auth?.user?.uid : '_');
  const likedUserRef = doc(postRef, 'likedUsers', auth?.user ? auth?.user?.uid : '_');
  const likedPostRef = doc(userRef, 'likedPosts', data.id);

  const likedUserDoc = useFirestore<Post>(likedUserRef);
  const isLiked = !!likedUserDoc.data && !likedUserDoc.isLoading; // 読み込み済みでデータがあればいいね済み
  const canMutate = !likedUserDoc.isLoading; // 読み込み中はいいねできない

  const mutateToggle = () => {
    if (canMutate) {
      isLiked ? mutateUnLikeBatch() : mutateLikeBatch();
    }
  };

  const mutateLikeBatch = () => {
    // いいねをつけたユーザーのリストに自分を追加
    batch.set(likedUserRef, { ...timestampTemp });

    // 自分のいいねした投稿リストに追加
    batch.set(likedPostRef, {
      originRef: postRef,
      ...timestampTemp,
    });

    batch.update(postRef, { likeCount: increment(1) });

    LikePostBatch.mutate();
  };

  const mutateUnLikeBatch = () => {
    // いいねをつけたユーザーのリストから自分を削除
    batch.delete(likedUserRef);

    // 自分のいいねした投稿リストから削除
    batch.delete(likedPostRef);

    batch.update(postRef, { likeCount: increment(-1) });

    LikePostBatch.mutate();
  };

  return {
    ...LikePostBatch,
    mutateToggle,
    isLiked,
    canMutate,
  };
};
2
0
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
2
0