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따로 돌리고 통신 따로 했다 ㅋㅋㅋ