23/12/26 심화팀과제 내가한거 리뷰
2023. 12. 27. 08:44ㆍ카테고리 없음
<Header.jsx>
import React from 'react';
import styled from 'styled-components';
import PinImage from '../asset/pin.png';
import {useNavigate} from 'react-router-dom';
import Button from '../components/UI/Button';
import {useDispatch, useSelector} from 'react-redux';
import {logout} from '../redux/modules/Auth';
import {getAuth} from 'firebase/auth';
import {toast} from 'react-toastify';
export default function Header() {
const navigate = useNavigate();
const dispatch = useDispatch();
const {displayName, isLogin} = useSelector(state => state.auth);
const auth = getAuth(); //여기서 이 auth는 로그아웃 로직에 쓸 auth인데
//getAuth를 임포트 하는게 아니라 return ( //firebase.js에에서 익스포트한 초기화한 auth를 임포트해서 써야 되는거 같다.
<>
<Nav>
<Left
onClick={() => {
navigate('/'); //left는 홈버튼 태그인데 누르면 홈으로감
}}
>
<ScHomeBtn>
<img src={PinImage} alt="" />
<h1>Let's Fix</h1>
</ScHomeBtn>
</Left>
<Right>
{isLogin ? ( //로그인 상태가 트루이면 보여주는 태그와
<>
<Button
onClick={() => {
navigate('/profile');
}}
>
{displayName}님의 마이페이지
</Button>
<Button
onClick={() => {
dispatch(logout()); //로그아웃 정보로 툴킷의 스테이트 변경
navigate('/');
auth.signOut(); //로그아웃 로직
toast.success('로그아웃되었습니다');
}}
>
로그아웃
</Button>
</>
) : (
<>
<Button
onClick={() => {
navigate('/register'); // 로그인이 안되있으면 삼항 연산자가 false가 되면서
}}
>
회원가입 //회원가입과 로그인페이지로 가는 버튼 활성화
</Button>
<Button
onClick={() => {
navigate('/login');
}}
>
로그인
</Button>
</>
)}
</Right>
</Nav>
</>
);
}
const Nav = styled.nav`
height: 100px;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
background-color: var(--deep-blue);
font-size: 40px;
font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
padding: 0px 20px;
`;
const Left = styled.div`
width: 50%;
height: 100px;
display: flex;
justify-content: flex-start;
align-items: center;
border-radius: 10px;
padding: 0 10px;
`;
const ScHomeBtn = styled.div`
display: flex;
justify-content: flex-start;
align-items: center;
width: 50%;
cursor: pointer;
@media only screen and (max-width: 980px) {
width: 70%;
}
img {
width: 68px;
height: auto;
margin-right: 10px;
}
&:hover {
cursor: pointer;
transform: scale(1.05);
transition: all 0.2s;
}
`;
const Right = styled.div`
width: 50%;
display: flex;
justify-content: flex-end;
align-items: center;
gap: 10px;
button:last-child {
width: 20%;
}
`;
<Homepage.jsx>
////import React from 'react';
import styled, {css} from 'styled-components';
import AddNew from '../components/AddNew';
import {useNavigate} from 'react-router';
import {useSelector} from 'react-redux';
import {useReadFirestore} from '../components/UI/CustomHook';
export default function Homepage() {
const navigate = useNavigate();
const {isLogin} = useSelector(state => state.auth); //구조분해 할당으로 isLogin만 툴킷에서 구독
useReadFirestore();
const list = useSelector(state => state.fixList); //여기는 fixLIst 툴킷의 state인 list 바로 끌어다 씀
return (
<ScBody>
<ScFixbar>
<span>최근 Fix 한 곳</span>
{isLogin ? <AddNew /> : <></>} //로그인 상태여야지만 사용할수 있는 새로운 글 남기는 태그
</ScFixbar>
<ScListWrapper>
{list.map(item => {
return (
<ScList key={item.id} onClick={() => navigate(`/detail/${item.id}`)}> //해당아이디를 파람스로 사용해서
<ScPhotoWrapper> //디테일 페이지로 넘어가는 버튼
<img src={item.image_url} alt="" />
</ScPhotoWrapper>
<ScUserInfo>
<ScAvatar>
{' '}
<img src={item.photoURL} alt="" />
</ScAvatar>
<ScNicknameAndDate>
<p>{item.displayName}</p>
<time>{item.date}</time>
</ScNicknameAndDate>
</ScUserInfo>
<ScContent>
<h1>{item.title}</h1>
<h3>{item.addrInput}</h3>
<h2>{item.content}</h2>
</ScContent>
</ScList>
);
})}
</ScListWrapper>
</ScBody>
);
}
const ScBody = styled.div`
display: flex;
flex-direction: column;
align-items: center;
padding: 0 70px;
`;
................ css 더 있는데 일단 지움
<fixlist.js>
import {createSlice} from '@reduxjs/toolkit';
const initialState = [];
const listSlice = createSlice({
name: 'fixList',
initialState,
reducers: {
setList: (state, action) => { 페이로드로 list 그잡채를 받아서 state 변경
return action.payload;
},
addList: (state, action) => {
state.unshift(action.payload); //이머가 있으니까 언쉬프트로 배열앞에 페이로드로
}, //받은 새 리스트 추가 로직
},
});
export default listSlice.reducer;
export const {setList, addList} = listSlice.actions;
<customhook.jsx> // 파일 분리를 나름 해봤다 ㅋㅋ
import {collection, addDoc, setDoc, getDocs, deleteDoc, doc} from 'firebase/firestore';
import {auth, db} from '../../shared/firebase'; //auth 잘 끌어 왔고
import {setList} from '../../redux/modules/fixList';
import {useDispatch, useSelector} from 'react-redux';
import {useEffect} from 'react';
import Button from './Button';
import {toast} from 'react-toastify';
export const useReadFirestore = async () => {
const dispatch = useDispatch();
const dataReading = async () => {
const querySnapshot = await getDocs(collection(db, 'fixs')); //파이어베이스에 저장된 lisx get통신
let dataArr = [];
querySnapshot.forEach(doc => { //통신으로 받은 쿼리스냅샷이란list에서 data만 뽑는다.
const data = doc.data();
dataArr.push({...data, id: doc.id}); //파이어베이스는 자체적으로 주어지는 id를 따로 주는데
//번거로운 암튼 이거를 포함한 list를 만들어 빈배열에 새배열 생성 dataArr = dataArr.sort((a, b) => b.createdAt - a.createdAt);
}); //시간순으로 정렬하기 위한 로직
dispatch(setList(dataArr));
};
useEffect(() => {
dataReading(); //첫 랜더링시 list를 get하기위한 유즈 이펙트
}, []);
};
export const Update = async (photoURL, displayName, filteredList, dispatch, dataReading) => {
try {
const updatePromises = filteredList.map(async item => { //프로필 업데이트를 위한 정보를
await setDoc(doc(db, 'fixs', `${item.id}`), { //파라미터로 받은다음
...item,
displayName, //요거로 map함수를 돌며 특정 아이디의
photoURL, //특정 list 하나씩 닉네임과 포토를 바꿔줌
});
});
await Promise.all(updatePromises); //이거 처음 써봤는데 프로미스 얼 안에는 배열이 들어가고
dataReading(); //동시에 프로미스를 수행해서 가장늦은애를 기다려줌
//여러 프로미스중 하나라도 오류나면 다오류 dispatch(
setList(
filteredList.map(item => ({ //스테이트도 변경된 리스트로 상태변화를 주기위한 로직
...item,
displayName,
photoURL,
})),
),
);
toast.success('수정성공!', {
position: 'top-center',
autoClose: 3000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: false,
progress: undefined,
theme: 'colored',
});
} catch (error) {
console.error('업데이트 중 오류 발생:', error);
}
};
export default function EditBtn() { //한번 만들어본 수정 버튼
const list = useSelector(state => state.fixList);
const {isLogin, displayName, uid, photoURL, email} = useSelector(state => state.auth); //이 값들을 구독하고
const filteredList = list.filter(item => { //필터를 써서 해당배열중에 list중 uid가 일치하는 넘들을
return item.uid == auth.currentUser.uid; //필터링
});
const dispatch = useDispatch(); //얘는 컴포넌트라서 유즈 디스패치 가능
const dataReading = async () => {
const querySnapshot = await getDocs(collection(db, 'fixs'));
let dataArr = [];
querySnapshot.forEach(doc => {
const data = doc.data();
// console.log(data, ' 이게 독 아이디');
dataArr.push({...data, id: doc.id});
// console.log(data.createdAt, '이게그거');
dataArr = dataArr.sort((a, b) => b.createdAt - a.createdAt); //지금 보니 로직 한번 더 썻네? ㅋㅋㅋ
});
dispatch(setList(dataArr));
};
return <Button onClick={() => Update(photoURL, displayName, filteredList, dispatch, dataReading)}>수정적용</Button>;
} //요건 해당 버튼을 태그로 쓰기위한 리턴
// 마지막 thunk를 썻다면 좀더 깔끔하고 지저분하지 않았을텐데 당시에는 thunk가 너무 낯설어서
//그냥 RTK에 state따로 돌리고 통신 따로 했다 ㅋㅋㅋ