yamoo9 / likelion-FEQA

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

[LAB-5] firebase에서 데이터 불러올 때 undefined 발생 #247

Closed seoohyeon closed 1 year ago

seoohyeon commented 1 year ago

질문 작성자

김서현

문제 상황

242 이슈 답변에 따라 코드를 수정해보았으나,

데이터를 firebase에서 가져오는 과정에서 처음에 undefined가 떠서 에러가 뜨는 것 같아 질문을 남깁니다.

image

프로젝트 저장소 URL

https://github.com/React-Project-lab5/React-Project-lab5
develop브랜치

새로 수정된 src라 이거 받으셔서 하시면 됩니다!!

VITE_API_KEY = AIzaSyD3qb4s7ciOVHXQTOYTZsoHs5gjBGQNDWY VITE_AUTH_DOMAIN = likelion-seo-first.firebaseapp.com VITE_PROJECT_ID = likelion-seo-first VITE_STORAGE_BUCKET = likelion-seo-first.appspot.com VITE_MESSAGE_SENDER_ID = 591976348589 VITE_APP_ID = 1:591976348589:web:9e41f2f24e6e1125d17f24

VITE_SERVICE_KEY=M4E1Vdrm%2Ba%2FjVfatJPdEbBibLbYBeDaGm7tgF%2BxisSinPYTEEBDkFgSO0gmqjAZGOzfrSffYbsOF3XshtdL6lg%3D%3D

환경 정보

yamoo9 commented 1 year ago

문제 원인

Firebase에서 사용자 정보를 응답 받기 전에 화면에 렌더링 하려 시도하기 때문입니다.

문제 해결

문제 해결을 위해 로딩(isLoading) 상태를 추가해 관리해야 합니다.

const [isLoading, setIsLoading] = useState(true);

// 사용자(user) 정보가 업데이트 될 때마다 이펙트 콜백
useEffect(() => {
  // 사용자 정보가 존재하면 로딩 상태를 false로 변경
  if (user) {
    setIsLoading(false);
  }
}, [user]);

isLoading 상태가 true인 동안에는 로딩 스피너를 화면에 표시해 사용자에게 로딩 중임을 안내합니다.

if (isLoading) {
  return <div role="alert"> 사용자 정보 로딩 중... </div>;
}

수정이 반영되면 아래 화면처럼 데이터 응답 이후에 정상적으로 페이지가 렌더링 됩니다. 😀

가이드 파일

MyPage.tsx 컴포넌트 코드만 수정 했으므로 아래 코드를 복사/붙여넣기 한 후 확인해보세요.

MyPage.tsx ```tsx import classes from './MyPage.module.scss'; import { Input, ProfileImage } from '@/components'; import { useState, useEffect, useCallback } from 'react'; import { signOut, deleteUser, updateProfile, updateEmail, EmailAuthProvider, reauthenticateWithCredential, } from '@firebase/auth'; import { auth } from '@/firebase/auth'; import { useNavigate } from 'react-router-dom'; import { doc, collection, getDoc, setDoc, deleteDoc, } from '@firebase/firestore'; import { db } from '@/firebase/app'; import { useDocumentTitle } from '@/hooks/useDocumentTitle'; import { debounce } from 'lodash'; export default function MyPage() { useDocumentTitle('슬기로운 N밥 생활 | 마이 페이지'); const navigation = useNavigate(); const [isEditing, setIsEditing] = useState(false); const [name, setName] = useState(''); const [email, setEmail] = useState(''); const [phoneNumber, setPhoneNumber] = useState(''); const [address, setAddress] = useState(''); const user = auth.currentUser; const [isLoading, setIsLoading] = useState(true); useEffect(() => { if (user) { setIsLoading(false); } }, [user]); useEffect(() => { const unsub = auth.onAuthStateChanged((user) => { if (user) { const getUserRef = doc(collection(db, 'users'), user.uid); getDoc(getUserRef).then((doc) => { if (doc.exists()) { const userData = doc.data(); setName(userData.displayName); setEmail(userData.email); setPhoneNumber(userData.phoneNumber); setAddress(userData.address); } else { user.providerData.forEach((profile) => { setName(profile.displayName); setEmail(profile.email); }); } }); } }); return unsub; }, []); useEffect(() => { const userController = document.getElementById('memberController'); if (!userController) { return; } if (userController) { userController.style.color = 'red'; } else { userController.style.color = 'black'; } }, [isEditing]); /* ------------------------------- '회원정보수정' 클릭 ------------------------------ */ const handleEditClick = () => { if (user.providerData[0].providerId === 'google.com') { alert('구글 및 카카오 사용자는 회원정보수정이 불가합니다!'); } else { setIsEditing(!isEditing); } }; /* -------------------------------- 수정 완료 클릭 -------------------------------- */ const handleSaveClick = () => { /* ----- Firestore 업데이트 ----- */ const getUserRef = doc(collection(db, 'users'), user.uid); setDoc( getUserRef, { displayName: name, email: email, phoneNumber: phoneNumber, address: address, }, { merge: true } ).then(() => { setIsEditing(false); }); /* ----- currentUser 업데이트 (displayName, email) ----- */ updateProfile(user, { displayName: name }) .then(() => { console.log('사용자 이름 변경 완료!'); }) .catch((error) => { console.log('사용자 이름 변경 실패', error); }); updateEmail(user, email) .then(() => { getDoc(getUserRef).then((doc) => { if (doc.exists()) { const userProvidedPassword = doc.data().password; const credential = EmailAuthProvider.credential( user.email, userProvidedPassword ); reauthenticateWithCredential(user, credential) .then(() => { console.log('재인증에 성공하였습니다.'); alert('회원 정보가 수정되었습니다!'); }) .catch((error) => { console.log('재인증에 실패하였습니다.', error); }); } }); console.log('사용자 이메일 변경 완료!'); }) .catch((error) => { console.log('사용자 이메일 변경 실패!', error); }); }; /* ---------------------------------- 로그아웃 ---------------------------------- */ const handleSignOut = () => { signOut(auth); alert('로그아웃이 되었습니다.'); navigation('/'); }; /* ---------------------------------- 회원탈퇴 ---------------------------------- */ const handleSignDropOut = () => { const user = auth.currentUser; deleteDoc(doc(db, 'users', user.uid)); deleteUser(user) .then(() => { alert('회원 탈퇴 성공!'); navigation('/'); }) .catch((error) => { console.log('회원 탈퇴 실패!', error); }); }; /* -------------------------- debounce 함수 직접 제작 코드 -------------------------- */ const editName = debounce((e) => { console.log(e.target.value); setName(e.target.value); }, 500); const editEmail = debounce((e) => { console.log(e.target.value); setEmail(e.target.value); }, 500); const editPhoneNumber = debounce((e) => { console.log(e.target.value); setPhoneNumber(e.target.value); }, 500); const editAddress = debounce((e) => { console.log(e.target.value); setAddress(e.target.value); }, 500); if (isLoading) { return
사용자 정보 로딩 중...
; } return (

마이페이지

| |
); } ```