yamoo9 / likelion-FEQA

질문/답변 — 프론트엔드 스쿨, 멋사
29 stars 9 forks source link

[LAB-8] 공통된 정보로 컬렉션 값을 가져올때의 중복사항 #266

Closed rodwnl closed 1 year ago

rodwnl commented 1 year ago

질문 작성자

이경주

문제 상황

https://github.com/yamoo9/likelion-FEQA/issues/174 에서 했던 질문과 비슷한 류 입니다

image image product의 userId로 users의 userId과 같은 것을 찾아 정보를 불러오는 코드를 야무쌤 답변 코드에서 수정하여 적용한 것 입니다.

import {useEffect} from "react";
import {atom, selector, useRecoilState, useRecoilValue} from "recoil";
import {app} from "@/firebase/app";
import {getFirestore, collection, getDocs, query, limit, where} from "firebase/firestore";

const productsAtom = atom({
  key: "products",
  default: [],
});

const productsExcludeIdAtom = atom({
  key: "productsExcludeId",
  default: [],
});

const isLoadingSelector = selector({
  key: "productsIsLoading",
  get: ({get}) => {
    const stores = get(productsAtom);
    return stores.length === 0;
  },
});

const errorSelector = selector({
  key: "productsError",
  get: ({get}) => {
    const stores = get(productsAtom);
    if (stores.length === 0) {
      return "Error fetching stores";
    } else {
      return null;
    }
  },
});

export function useProducts(excludeId, limitCount = 99) {
  const [productsState, setProductsState] = useRecoilState(!excludeId ? productsAtom : productsExcludeIdAtom);
  const isLoading = useRecoilValue(isLoadingSelector);
  const error = useRecoilValue(errorSelector);

  useEffect(() => {
    const db = getFirestore(app);
    const productsRef = collection(db, "Products");

    let q = query(productsRef, limit(limitCount));

    if (excludeId) {
      q = query(productsRef, where("id", "!=", excludeId), limit(limitCount));
    }

    getDocs(q).then((querySnapshot) => {
      const products = [];
      const queryPromises = [];

      querySnapshot.forEach((doc) => {
        const product = {id: doc.id, ...doc.data()};
        const userId = product.userId;
        console.log(product); // 문제 없음
        products.push(product);
        const queryPromise = new Promise(async (resolve, reject) => {
          const usersSnapshot = await getDocs(query(collection(db, "users"), where("userId", "==", userId)));
          resolve(usersSnapshot.docs);
        });

        queryPromises.push(queryPromise);
      });

      Promise.all(queryPromises)
        .then((userDocs) => {
          const users = [];
          console.log(userDocs); // 여기서 부터 중복?
          userDocs.forEach(([doc]) => {
            users.push({id: doc.id, ...doc.data()});
          });
          return users;
        })
        .then((users) => {
          return users.map((user) => {
            return {...products.find((product) => product.userId === user.id), ...user};
          });
        })
        .then((processedProducts) => {
          const shuffledProducts = processedProducts.sort(() => Math.random() - 0.5);
          setProductsState(shuffledProducts);
        });
    });
  }, [setProductsState, excludeId, limitCount]);

  return {isLoading, error, productsState};
}

image 추가되야할 정보들은 (users의 정보) 잘 추가 되나 계속 아이템이 중복이 됩니다

const queryPromise = new Promise(async (resolve, reject) => {
          const usersSnapshot = await getDocs(query(collection(db, "users"), where("userId", "==", userId)));
          resolve(usersSnapshot.docs);
        });

전에 console.log(product)를 했을 땐 중복된 데이터가 없는 것을 보아 query문이 들어간 후부터 문제인 것 같은데 왜 그런지 모르겠습니다. users에서 userId와 같은 곳의 데이터를 비동기 방식을 통해 전개연산자로 추가하는게 아닌가요? 제가 코드 해석이 덜되서 그런지 설명해주시면 감사하겠습니다..

프로젝트 저장소 URL

https://github.com/rodwnl/SAJA-Market/tree/feature/kyoungju/home

환경 정보

yamoo9 commented 1 year ago

문제 해결

UseProducts.jsx 파일을 아래와 같이 수정해보세요. 😃 (주석 참고)

export function useProducts(excludeId, limitCount = 99) {
  const [productsState, setProductsState] = useRecoilState(!excludeId ? productsAtom : productsExcludeIdAtom);
  const isLoading = useRecoilValue(isLoadingSelector);
  const error = useRecoilValue(errorSelector);

  useEffect(() => {
    const db = getFirestore(app);
    const productsRef = collection(db, "Products");

    let q = query(productsRef, limit(limitCount));

    if (excludeId) {
      q = query(productsRef, where("id", "!=", excludeId), limit(limitCount));
    }

    getDocs(q).then((querySnapshot) => {
      const products = [];
      const queryPromises = [];

      querySnapshot.forEach((document) => {
        const product = {id: document.id, ...document.data()};
        const userId = product.userId;

        products.push(product);
        // userId와 일치하는 users 콜렉션 문서 가져오기 (비동기 요청) Promise 반환
        queryPromises.push(getDoc(doc(db, `users/${userId}`)));
      });

      // Promise.all을 사용해 queryPromises 비동기 요청 집합이 모두 완료 되면 콜백
      Promise.all(queryPromises)
        .then((userDocs) => {
          const users = [];

          userDocs.forEach((doc) => {
            users.push({id: doc.id, ...doc.data()});
          });

          return users;
        })
        .then((users) => {
          // ✅ 상품 순환 후, 상품의 userId와 사용자 id가 일치하는 사용자 정보 병합
          return products.map((product) => {
            return {...product, ...users.find((user) => user.id === product.userId)};
          });
        })
        .then((processedProducts) => {
          const shuffledProducts = processedProducts.sort(() => Math.random() - 0.5);
          setProductsState(shuffledProducts);
        });
    });
  }, [setProductsState, excludeId, limitCount]);

  return {isLoading, error, productsState};
}