ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • #9 Frontend: Profile
    인스타그램클론(노마드코더강의)/#9 Frontend: Profile 2020. 4. 21. 15:05

    #9.0 Profile Screen part One~ #9.1 Two

     

    Profile 부분을 만들어보자.

    Routes.js 파일에서 url 주소/:username을 입력 하면 Profile Route파일로 이동하게 된다.

    해당 /:username은 params에 담기게 된다. 이게 무슨말인지 이어서 보자.

     

    >Routes.js

    const LoggedInRoutes = () => (
      <Switch>
        <Route exact path="/" component={Feed} />
        <Route path="/explore" component={Explore} />
        <Route path="/search" component={Search} />
        <Route path="/:username" component={Profile} />
      </Switch>
    );

     

    >ProfileContainer.js

    import React from "react";
    import { gql } from "apollo-boost";
    import { withRouter } from "react-router-dom";
    import { useQuery } from "react-apollo-hooks";
    import ProfilePresenter from "./ProfilePresenter";
    
    const GET_USER = gql`
      query seeUser($username: String!) {
        seeUser(username: $username) {
          id
          avatar
          username
          fullName
          isFollowing
          isSelf
          bio
          followingCount
          followersCount
          postsCount
          posts {
            id
            files {
              url
            }
            likeCount
            commentCount
          }
        }
      }
    `;
    
    export default withRouter(
      ({
        match: {
          params: { username },
        },
      }) => {
        const { data, loading } = useQuery(GET_USER, { variables: { username } });
        return <ProfilePresenter loading={loading} data={data} />;
      }
    );
    

    gql를 통해 seeUser값을 받아온다. 여기서 특징은, withRouter를 사용하여 params에 있는 값을 가지고 올 수 있다. 여기서 username은 위 Routes.js에서 정의한 username값이다.

     

    >ProfilePresenter.js

    import React from "react";
    import styled from "styled-components";
    import { Helmet } from "react-helmet";
    import Loader from "../../Components/Loader";
    import Avatar from "../../Components/Avatar";
    import FatText from "../../Components/FatText";
    import FollowButton from "../../Components/FollowButton";
    import SquarePost from "../../Components/SquarePost";
    
    const Wrapper = styled.div`
      min-height: 100vh;
    `;
    
    const Header = styled.header`
      display: flex;
      align-items: center;
      justify-content: space-around;
      width: 80%;
      margin: 0 auto;
      margin-bottom: 40px;
    `;
    
    const HeaderColumn = styled.div``;
    
    const UsernameRow = styled.div`
      display: flex;
      align-items: center;
    `;
    
    const Username = styled.span`
      font-size: 26px;
      display: block;
    `;
    
    const Counts = styled.ul`
      display: flex;
      margin: 15px 0px;
    `;
    
    const Count = styled.li`
      font-size: 16px;
      &:not(:last-child) {
        margin-right: 10px;
      }
    `;
    
    const FullName = styled(FatText)`
      font-size: 16px;
    `;
    
    const Bio = styled.p`
      margin: 10px 0px;
    `;
    
    const Posts = styled.div`
      display: grid;
      grid-template-columns: repeat(4, 200px);
      grid-template-rows: 200px;
      grid-auto-rows: 200px;
    `;
    
    export default ({ loading, data }) => {
      if (loading === true) {
        return (
          <Wrapper>
            <Loader />
          </Wrapper>
        );
      } else if (!loading && data && data.seeUser) {
        console.log(data);
        const {
          seeUser: {
            id,
            avatar,
            username,
            fullName,
            isFollowing,
            isSelf,
            bio,
            followingCount,
            followersCount,
            postsCount,
            posts,
          },
        } = data;
        return (
          <Wrapper>
            <Helmet>
              <title>{username} | Prismagram</title>
            </Helmet>
            <Header>
              <HeaderColumn>
                <Avatar size="lg" url={avatar} />
              </HeaderColumn>
              <HeaderColumn>
                <UsernameRow>
                  <Username>{username}</Username>{" "}
                  {!isSelf && <FollowButton isFollowing={isFollowing} id={id} />}
                </UsernameRow>
                <Counts>
                  <Count>
                    <FatText text={String(postsCount)} /> posts
                  </Count>
                  <Count>
                    <FatText text={String(followersCount)} /> followers
                  </Count>
                  <Count>
                    <FatText text={String(followingCount)} /> following
                  </Count>
                </Counts>
                <FullName text={fullName} />
                <Bio>{bio}</Bio>
              </HeaderColumn>
            </Header>
            <Posts>
              {posts &&
                posts.map((post) => (
                  <SquarePost
                    key={post.id}
                    likeCount={post.likeCount}
                    commentCount={post.commentCount}
                    file={post.files[0]}
                  />
                ))}
            </Posts>
          </Wrapper>
        );
      }
      return null;
    };
    

    #9.2 Log Out and Conclusions

     

    로그아웃 기능을 만들어보자. (Profile안에)

     

    >ProfileContainer.js

    import React from "react";
    import { gql } from "apollo-boost";
    import withRouter from "react-router-dom/withRouter";
    import { useQuery, useMutation } from "react-apollo-hooks";
    import ProfilePresenter from "./ProfilePresenter";
    
    const GET_USER = gql`
      query seeUser($username: String!) {
        seeUser(username: $username) {
          id
          avatar
          username
          fullName
          isFollowing
          isSelf
          bio
          followingCount
          followersCount
          postsCount
          posts {
            id
            files {
              url
            }
            likeCount
            commentCount
          }
        }
      }
    `;
    
    const LOG_OUT = gql`
      mutation logUserOut {
        logUserOut @client
      }
    `;
    
    export default withRouter(
      ({
        match: {
          params: { username },
        },
      }) => {
        const { data, loading } = useQuery(GET_USER, { variables: { username } });
        const [logOut] = useMutation(LOG_OUT);
        return <ProfilePresenter loading={loading} logOut={logOut} data={data} />;
      }
    );
    

    로그아웃에 대한 mutation 을 만들어 두었다. (버튼 클릭 시 onClick 이벤트로 발동)

    이는 localState를 이용한 것이다. (logInUser와 같이)

     

    >ProfilePresenter.js

    import React from "react";
    import styled from "styled-components";
    import { Helmet } from "react-helmet";
    import Loader from "../../Components/Loader";
    import Avatar from "../../Components/Avatar";
    import FatText from "../../Components/FatText";
    import FollowButton from "../../Components/FollowButton";
    import SquarePost from "../../Components/SquarePost";
    import Button from "../../Components/Button";
    
    const Wrapper = styled.div`
      min-height: 100vh;
    `;
    
    const Header = styled.header`
      display: flex;
      align-items: center;
      justify-content: space-around;
      width: 80%;
      margin: 0 auto;
      margin-bottom: 40px;
    `;
    
    const HeaderColumn = styled.div``;
    
    const UsernameRow = styled.div`
      display: flex;
      align-items: center;
    `;
    
    const Username = styled.span`
      font-size: 26px;
      display: block;
    `;
    
    const Counts = styled.ul`
      display: flex;
      margin: 15px 0px;
    `;
    
    const Count = styled.li`
      font-size: 16px;
      &:not(:last-child) {
        margin-right: 10px;
      }
    `;
    
    const FullName = styled(FatText)`
      font-size: 16px;
    `;
    
    const Bio = styled.p`
      margin: 10px 0px;
    `;
    
    const Posts = styled.div`
      display: grid;
      grid-template-columns: repeat(4, 200px);
      grid-template-rows: 200px;
      grid-auto-rows: 200px;
    `;
    
    export default ({ loading, data, logOut }) => {
      if (loading === true) {
        return (
          <Wrapper>
            <Loader />
          </Wrapper>
        );
      } else if (!loading && data && data.seeUser) {
        const {
          seeUser: {
            id,
            avatar,
            username,
            fullName,
            isFollowing,
            isSelf,
            bio,
            followingCount,
            followersCount,
            postsCount,
            posts,
          },
        } = data;
        return (
          <Wrapper>
            <Helmet>
              <title>{username} | Prismagram</title>
            </Helmet>
            <Header>
              <HeaderColumn>
                <Avatar size="lg" url={avatar} />
              </HeaderColumn>
              <HeaderColumn>
                <UsernameRow>
                  <Username>{username}</Username>{" "}
                  {isSelf ? (
                    <Button onClick={logOut} text="Log Out" />
                  ) : (
                    <FollowButton isFollowing={isFollowing} id={id} />
                  )}
                </UsernameRow>
                <Counts>
                  <Count>
                    <FatText text={String(postsCount)} /> posts
                  </Count>
                  <Count>
                    <FatText text={String(followersCount)} /> followers
                  </Count>
                  <Count>
                    <FatText text={String(followingCount)} /> following
                  </Count>
                </Counts>
                <FullName text={fullName} />
                <Bio>{bio}</Bio>
              </HeaderColumn>
            </Header>
            <Posts>
              {posts &&
                posts.map((post) => (
                  <SquarePost
                    key={post.id}
                    likeCount={post.likeCount}
                    commentCount={post.commentCount}
                    file={post.files[0]}
                  />
                ))}
            </Posts>
          </Wrapper>
        );
      }
      return null;
    };
    

     

Designed by Tistory.