4주차 - 상세 페이지 연결하기 + fireStore 복합 쿼리
본문 바로가기
항해 중/5주차 리액트 심화반

4주차 - 상세 페이지 연결하기 + fireStore 복합 쿼리

by 은돌1113 2021. 12. 2.

1. 페이지 링크 연결하기

 

- 목록 페이지에서 _onClick 넘겨주기

// PostList.js
import React from "react";
import {useSelector, useDispatch} from "react-redux";

import Post from "../components/Post";
import {actionCreators as postActions} from "../redux/modules/post";
import InfinityScroll from "../shared/InfinityScroll";
import {Grid} from "../elements";

const PostList = (props) => {
    const dispatch = useDispatch();
    const post_list = useSelector((state) => state.post.list);
    const user_info = useSelector((state) => state.user.user);
    const is_loading = useSelector((state) => state.post.is_loading);
    const paging = useSelector((state) => state.post.paging);

    const {history} = props;
    
    React.useEffect(() => {

        if(post_list.length === 0){
             dispatch(postActions.getPostFB());
        }
       
    }, []);

    return (
      <React.Fragment>
        <Grid bg={"#EFF6FF"} padding="20px 0px">
          {/* <Post/> */}
          <InfinityScroll
            callNext={() => {
              dispatch(postActions.getPostFB(paging.next));
            }}
            is_next={paging.next ? true : false}
            loading={is_loading}
          >
            {post_list.map((p, idx) => {
              if (p.user_info.user_id === user_info?.uid) {
                return (
                  <Grid
                    bg="#ffffff"
                    margin="8px 0px"
                    key={p.id}
                    _onClick={() => {
                      history.push(`/post/${p.id}`);
                    }}
                  >
                    <Post key={p.id} {...p} is_me />
                  </Grid>
                );
              } else {
                return (
                  <Grid
										key={p.id}
                    bg="#ffffff"
                    _onClick={() => {
                      history.push(`/post/${p.id}`);
                    }}
                  >
                    <Post {...p} />
                  </Grid>
                );
              }
            })}
          </InfinityScroll>
        </Grid>
      </React.Fragment>
    );
}

export default PostList;

 

- Grid에 onClick() 속성을 추가하여 눌렀을 때 상세 페이지로 이동하기

import React from "react";
import styled from "styled-components";

const Grid = (props) => {
  const { is_flex, width, margin, padding, bg, children, center, _onClick } = props;

  const styles = {
      is_flex: is_flex,
      width: width,
      margin: margin,
      padding: padding,
      bg: bg,
      center: center,
  };
  return (
    <React.Fragment>
      <GridBox {...styles} onClick={_onClick}>{children}</GridBox>
    </React.Fragment>
  );
};

Grid.defaultProps = {
  chidren: null,
  is_flex: false,
  width: "100%",
  padding: false,
  margin: false,
  bg: false,
  center: false,
  _onClick: () => {}
};

const GridBox = styled.div`
  width: ${(props) => props.width};
  height: 100%;
  box-sizing: border-box;
  ${(props) => (props.padding ? `padding: ${props.padding};` : "")}
  ${(props) => (props.margin ? `margin: ${props.margin};` : "")}
  ${(props) => (props.bg ? `background-color: ${props.bg};` : "")}
  ${(props) =>
    props.is_flex
      ? `display: flex; align-items: center; justify-content: space-between; `
      : ""}
  ${(props) => props.center? `text-align: center;`: ""}
`;

export default Grid;

2. 리덕스 연결하기

 

- 리덕스에 데이터 가져오기

import React from "react";
import Post from "../components/Post";
import CommentList from "../components/CommentList";
import CommentWrite from "../components/CommentWrite";

import {useSelector} from "react-redux";

const PostDetail = (props) => {
    const id = props.match.params.id;

    const user_info = useSelector((state) => state.user.user);

    const post_list = useSelector(store => store.post.list);
    
    const post_idx = post_list.findIndex(p => p.id === id);
    const post = post_list[post_idx];

    return (
        <React.Fragment>
            <Post {...post} is_me={post.user_info.user_id === user_info.uid}/>
            <CommentWrite/>
            <CommentList/>
        </React.Fragment>
    )
}

export default PostDetail;

3. 파이어 스터어에서 데이터 가져오기

더보기

👉 [링크를 타고 들어온다면...]

우리가 만든 사이트에 게시글 1개 링크를 타고 들어온다고 생각해볼까요? 😖

: 앗, 파이어 스토어에서 데이터 가져오는 건... 목록 페이지인데?

맞아요! 그럴 때는 데이터 가진 게 없으니 보여줄 게 없죠.

이럴 때는 해당 게시글 id를 이용해서 게시글 정보 하나만 호다닥 가져오게 합시다!

 

- 만약 리덕스에 데이터가 없으면?? 단일 데이터를 가져오자!

import React from "react";
import Post from "../components/Post";
import CommentList from "../components/CommentList";
import CommentWrite from "../components/CommentWrite";

import { Button, Text, Grid } from "../elements";
import { useSelector } from "react-redux";
import { firestore } from "../shared/firebase";

const PostDetail = (props) => {
  const id = props.match.params.id; // 파라미터로 넘어온 params의 id를 불러온다.
  const post_list = useSelector((state) => state.post.list); // 게시물 목록
  const user_info = useSelector((state) => state.user.user); // 사용자 정보
  const is_login = useSelector((state) => state.user.is_login); // 로그인 정보

  // 상세 페이지의 게시물 정보를 불러온다.
  const post_idx = post_list.findIndex((p) => p.id === id);
  const post_data = post_list[post_idx];
  // === const post = post_list.find(p => p.id === id)

  const [post, setPost] = React.useState(post_data ? post_data : null);
  const { history } = props;

  React.useEffect(() => {
    if (post) {
      // 포스트 정보가 있을 경우 안불러 온다.
      return;
    }

    // 단일 데이터 가져오기
    const postDB = firestore.collection("post");
    postDB
      .doc(id)
      .get()
      .then((doc) => {
        // 형식 맞추기
        let _post = doc.data();
        let post = Object.keys(_post).reduce(
          (acc, cur) => {
            if (cur.indexOf("user_") !== -1) {
              return {
                ...acc,
                user_info: { ...acc.user_info, [cur]: _post[cur] },
              };
            }
            return { ...acc, [cur]: _post[cur] };
          },
          { id: doc.id, user_info: {} }
        );

        setPost(post);
      });
  });

  // 로그인 안했을 경우 상세 페이지 볼 수 없게
  if (!is_login) {
    return (
      <Grid margin="100px 0px" padding="16px" center>
        <Text size="32px" bold>
          앗! 잠깐!
        </Text>
        <Text size="16px">로그인 후에만 글을 쓸 수 있어요!</Text>
        <Button
          _onClick={() => {
            history.replace("/login");
          }}
        >
          로그인 하러가기
        </Button>
      </Grid>
    );
  }

  return (
    <React.Fragment>
      {post && (
        <Post {...post} is_me={post.user_info.user_id === user_info.uid} />
      )}
      <CommentWrite />
      <CommentList />
    </React.Fragment>
  );
};

export default PostDetail;

4. firestore 복합 쿼리 쓰기

더보기

👉 [다음 시간을 위한 준비예요!]

복합쿼리는 설정하고 반영되기까지 시간이 꽤 걸리거든요! 다음 강의 가기 전, 미리 해두고 갈게요. 😉

복합쿼리가 뭐냐구요?

복합쿼리는 firestore에서 한 콜렉션의 여러 필드를 한 쿼리로 묶을 때 필요한 거예요.

→ 여러 필드 내용을 가지고 쿼리를 쓰고 싶을 때 쓴다!

 

 

- 대시보드 설정하기

- 복합 쿼리 쓰는 방법 미리보기!!

const getCommentFB = (post_id) => {
  return function (dispatch) {
    console.log("in get comment fb");

    firestore
      .collection("comment")
      **.where("post_id", "==", post_id)
      .orderBy("insert_dt", "desc")**
      .get()
      .then((docs) => {
        let _list = [];
        docs.forEach((doc) => {
          _list.push({ ...doc.data(), id: doc.id });
        });

        dispatch(setComment(_list, post_id));
      })
      .catch((err) => {
        console.log("get comment fb err :::", err);
      });
  };
};

5. 로그인을 안했을 경우 상세 페이지 띄우지 않고 알림창 띄우기

const is_login = useSelector((state) => state.user.is_login); // 로그인 정보
// postDetail.js
  // 로그인 안했을 경우 상세 페이지 볼 수 없게
  if (!is_login) {
    return (
      <Grid margin="100px 0px" padding="16px" center>
        <Text size="32px" bold>
          앗! 잠깐!
        </Text>
        <Text size="16px">로그인 후에만 글을 쓸 수 있어요!</Text>
        <Button
          _onClick={() => {
            history.replace("/login");
          }}
        >
          로그인 하러가기
        </Button>
      </Grid>
    );
  }

 

또는 

 

옵셔널 체이닝을 사용하면 된다.

A?.B일 경우 A의 값이 없으면 B를 불러오지 않아서 오류가 발생하지 않는다.

  return (
    <React.Fragment>
      {post && (
        <Post {...post} is_me={post.user_info.user_id === user_info?.uid} />
        // user_info?.uid : 옵셔널 체이닝을 사용하면 user_info에 값이 있을 경우 uid를 불러온다.
      )}
      <CommentWrite />
      <CommentList />
    </React.Fragment>
  );

댓글