- 이제 프로필에 My Profile이라고 보여지는 대신에 유저의 이름이 보이도록 변경할 것이다.
- 우선 router에서 navigation에 userObj를 보내고
naviagation에서 이를 받아 이메일을 출력하도록 하자
- 아래와 같이 프로필 이름을 바꾸었다.
현재는 displayName이 null이기 때문에 's Profile와 같이 뜨지만 곧 이름을 설정할 수 있도록 할 것이다.
// profile.js
import { authService, dbService } from "myFirebase";
import { collection, getDocs, orderBy, query, where } from "firebase/firestore";
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { updateProfile } from "firebase/auth";
// for Profile import
const Profile = ({ userObj }) => {
// redirect를 위한 navigate
const navigate = useNavigate();
const [newDisplayName, setNewDisplayName] = useState(userObj.displayName);
// 클릭하면 로그아웃
const onLogOutClick = () => {
authService.signOut();
navigate("/");
};
const onChange = (event) => {
const {
target: {value},
} = event;
setNewDisplayName(value);
};
const onSubmit = async (event)=> {
// 미 입력 방지
event.preventDefault();
// 변화가 있을 때만 업데이트
if(userObj.displayName !== newDisplayName) {
await updateProfile(userObj, {displayName: newDisplayName});
}
};
// 컬렉션중 userObj의 uid와 동일한createorId의 모든 문서를 내림차순으로 가져오는 쿼리
const getMyTweets = async() => {
const q = query(
collection(dbService, "tweets"),
where("creatorId", "==", userObj.uid),
orderBy("createdAt", "desc")
);
// 쿼리 결과값 가져오기
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id, "=>", doc.data());
});
};
useEffect(() => {
getMyTweets();
}, [])
return (
<>
<form onSubmit={onSubmit}>
<input
onChange={onChange}
type="text"
placeholder="Display Name"
value={newDisplayName}
/>
<input type="submit" value="Update Profile" />
</form>
<button onClick={onLogOutClick}>Log Out</button>
</>
);
};
export default Profile;
- 위 코드를 작성하고 빈 칸에 test를 적고 아래 update profile을 누르면 displayName이 바뀐다.
하지만 즉각 헤더에 반영되지 않고 새로고침을 해야 적용 되므로 개선이 필요하다.
- 위 문제점을 수정하기 위해 지금까지 왜 userObj를 여기저기서 공유해서 사용했는지 알아보자.
첫 번째 이유는 uid를 사용하기 위해서 이다.
하지만 이는 firebase의 authService.currnetUser.uid를 사용하면 된다.
이렇게 prop으로 공유하여 쓰면 가장 좋은 점은 소스를 하나로 통일할 수 있다는 점이다.
한 userObj state의 변화만으로 리액트가 새 정보를 인식 한 후 모든 요소들을 다시 렌더링 해주기 때문에 편리하다.
- usrObj은App.js에서 정의된 state이므로 App.js에서 새로고침을 하는 함수를 구현하고
refreshUser를 Router와 Profile에 보내주자
- 코드를 모두 적용시켰는데도 렌더링이 되지 않는 모습을 볼 수 있다.
이는 userObj의 크기가 너무 크기 떄문에 react가 정확한 정보를 인식하지 못하기 때문이다.
이를 해결하기 위해서는 object의 크기를 줄여주는 방법을 사용하거나
object.assgin을 사용하거나
user 대신 user...을 넘겨주는 방법이 있다.
여기서는 user...을 넘겨주는 방법을 사용할 것이다.
// App.js
import React, { useEffect, useState } from "react";
import AppRouter from "components/Router";
import { authService } from "myFirebase";
function App() {
// firebase가 프로그램을 초기화 하길 기다리기 위한 state
const [init, setInit] = useState(false);
// 로그인 여부 state
const [isLoggedIn, setIsLoggedIn] = useState(false);
// user 정보 state
const [userObj, setUserObj] = useState(null);
// userObj 새로고침
const refreshUser = () => {
const user = authService.currentUser;
setUserObj({...user});
}
// user의 변화를 듣고 listen
useEffect(() => {
authService.onAuthStateChanged((user) => {
if(user) {
setIsLoggedIn(true);
setUserObj({...user});
//console.log({...user});
}
else {
setIsLoggedIn(false);
}
setInit(true);
});
}, []);
return (
<>
{init ? <AppRouter
isLoggedIn={isLoggedIn}
userObj={userObj}
refreshUser={refreshUser}
/> : "Initializing..."}
<footer>© {new Date().getFullYear()} PONZ</footer>
</>
);
}
export default App;
// profile.js
import { authService, dbService } from "myFirebase";
import { collection, getDocs, orderBy, query, where } from "firebase/firestore";
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { updateProfile } from "firebase/auth";
// for Profile import
const Profile = ({ userObj, refreshUser }) => {
// redirect를 위한 navigate
const navigate = useNavigate();
const [newDisplayName, setNewDisplayName] = useState(userObj.displayName);
// 클릭하면 로그아웃
const onLogOutClick = () => {
authService.signOut();
navigate("/");
};
const onChange = (event) => {
const {
target: {value},
} = event;
setNewDisplayName(value);
};
const onSubmit = async (event)=> {
// 미 입력 방지
event.preventDefault();
// 변화가 있을 때만 업데이트
if(userObj.displayName !== newDisplayName) {
await updateProfile(authService.currentUser, {displayName: newDisplayName});
}
refreshUser();
};
// 컬렉션중 userObj의 uid와 동일한createorId의 모든 문서를 내림차순으로 가져오는 쿼리
const getMyTweets = async() => {
const q = query(
collection(dbService, "tweets"),
where("creatorId", "==", userObj.uid),
orderBy("createdAt", "desc")
);
// 쿼리 결과값 가져오기
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
//console.log(doc.id, "=>", doc.data());
});
};
useEffect(() => {
getMyTweets();
}, [])
return (
<>
<form onSubmit={onSubmit}>
<input
onChange={onChange}
type="text"
placeholder="Display Name"
value={newDisplayName}
/>
<input type="submit" value="Update Profile" />
</form>
<button onClick={onLogOutClick}>Log Out</button>
</>
);
};
export default Profile;
// router.js
import React, { useEffect, useState } from "react";
import { BrowserRouter, Routes, Route} from "react-router-dom";
import Navigation from "components/Navigation";
import Home from "routes/Home";
import Auth from "routes/Auth";
import Profile from "routes/Profile";
const AppRouter = ({isLoggedIn, userObj, refreshUser}) => {
return (
<BrowserRouter>
{isLoggedIn && <Navigation userObj={userObj}/>}
<Routes>
{isLoggedIn ? (
<>
<Route path ="/Home" element={<Home userObj={userObj}S/>}/>
<Route path ="/Profile" element={<Profile userObj={userObj} refreshUser={refreshUser} />}/>
</>
) : (
<>
<Route path ="/" element={<Auth />}/>
</>
)
}
</Routes>
</BrowserRouter>
);
};
export default AppRouter;
- 이제 수정사항이 바로 반영 되는 것을 볼 수 있다.
- ref) https://nomadcoders.co/nwitter/lectures/