GRAND 스택을 사용한 빠른 프로토타이핑 - 3부

GRAND 스택을 사용한 빠른 프로토타이핑: 3부



  • – 제품 소개, 기술 사양 및 그래프 모델.

  • - Apollo Graphql 서버 및 neo4j-graphql-js

  • - Apollo 클라이언트 및 React

  • 우리는 neo4j-graphql-js로 graphql 서버를 설정했습니다. 여기서 우리는 apollo-client가 React 애플리케이션에서 데이터 관리를 단순화하여 생산성 향상을 추가할 수 있는 방법을 고려할 것입니다.

    간편한 데이터 관리



    Apollo 클라이언트는 최신 스파의 가능한 모든 데이터 요구 사항을 다루는 훌륭한 데이터 관리 솔루션입니다. 그것이 우리 제품에 얼마나 도움이 되는지 생각해 봅시다. 다음은 잠재적 협업 후보인 애플리케이션의 홈 화면을 관리하기 위한 반응 구성 요소입니다.

    import React from "react";
    import { useQuery, gql } from "@apollo/client";
    import { Viewer } from "../generated/Viewer";
    import FireCard from "./FireCard";
    import { useHistory } from "react-router-dom";
    import NoResults from "./NoResults";
    import Loading from "./Loading";
    export const GET_USER = gql`
      query Viewer($id: ID!) {
        viewer(userId: $id) {
          userId
          matchCandidates(first: 10) {
            score
            user {
              name
              bio
              imageUrl
              userId
              skills {
                name
              }
            }
          }
        }
      }
    `;
    
    const Fire: React.FC<{ id: string }> = (props) => {
      const { loading, data, error, refetch, client } = useQuery<Viewer>(GET_USER, {
        variables: { id: props.id },
        notifyOnNetworkStatusChange: true,
      });
      const history = useHistory();
    
      // once viewer made a decision about (dis)liking, update the cache by removing cards viewer dis(liked)
      // pass function to FireCard component, which runs (dis)like mutation
      const update = (id: string) => {
        client.writeQuery({
          query: GET_USER,
          variables: { id: props.id },
          data: {
            viewer: {
              ...data?.viewer,
              matchCandidates: data?.viewer.matchCandidates.filter(
                (match) => match.user.userId !== id
              ),
            },
          },
        });
      };
    
      // refetch when swiped on all suggested candidates
      React.useEffect(() => {
        if (data && data.viewer.matches.length < 1) {
          refetch();
        }
      }, [data, refetch]);
    
      if (loading) {
        return <Loading>Loading potential candidates...</Loading>;
      }
      if (error || !data) {
        return (
          <h1 style={{ textAlign: "center", height: "100vh" }}>
            Try reloading the page...
          </h1>
        );
      }
    
      const { viewer } = data;
    
      if (viewer.matches.length < 1) {
        return (
          <NoResults
            buttonText={"Update preferences"}
            description="We don't have any candidates for you now. Try updating your preferences."
            action={() => history.push("/profile")}
          />
        );
      }
    
      return (
        <section className="f-col-center">
          <h1>Best candidates for {viewer.name}</h1>
          {viewer.matchCandidates.map((item) => (
            <FireCard
              key={item.user.userId}
              update={update}
              viewerId={props.id}
              score={item.score}
              {...item.user}
            />
          ))}
        </section>
      );
    };
    export default Fire;
    


    그곳에서 많은 일이 벌어지고 있습니다. 그러나 처음부터 시작합시다. 먼저 graphql 쿼리 GET_USER를 정의하여 구성 요소에 대한 데이터 요구 사항을 지정합니다. 2부에서는 User 유형에 matchCandidates 필드가 있었습니다. 여기서는 클라이언트가 잠재적인 일치 후보를 표시할 수 있도록 해당 데이터를 요청합니다. Apollo-client는 새로운 반응 후크 기능을 활용하기 위해 많은 유용한 반응 후크와 함께 제공됩니다. 함수 구성 요소의 첫 번째 줄은 useQuery 후크를 호출하고 쿼리 상태를 관리하기 위한 편리한 속성을 다시 가져옵니다. 다음으로 좋아요 또는 싫어요가 생성된 후 캐시를 업데이트하는 업데이트 기능이 있습니다. Apollo-client에는 변형의 업데이트 인수에 지정할 수 있는 멋진 cache.modify API가 있습니다. 문서에서 발췌한 내용은 다음과 같습니다.

    const [addComment] = useMutation(ADD_COMMENT, {
      update(cache, { data: { addComment } }) {
        cache.modify({
          fields: {
            comments(existingCommentRefs = [], { readField }) {
              const newCommentRef = cache.writeFragment({
                data: addComment,
                fragment: gql`
                  fragment NewComment on Comment {
                    id
                    text
                  }
                `,
              });
              return [...existingCommentRefs, newCommentRef];
            },
          },
        });
      },
    });
    


    상위 구성 요소에서 업데이트 기능을 지정하는 이유는 좋아하고 싫어하는 2개의 변형이 있으므로 FireCard 구성 요소에서 덜 번거롭기 때문입니다.

    const ADD_LIKE = gql`
      mutation AddLike($from: ID!, $to: ID!) {
        like(from: $from, to: $to) {
          matched
          matchId
          email
        }
      }
    `;
    const DISLIKE = gql`
      mutation AddDislike($from: ID!, $to: ID!) {
        dislike(from: $from, to: $to)
      }
    `;
    
    const FireCard: React.FC<Props> = ({
      imageUrl,
      bio,
      name,
      skills,
      userId,
      score,
      viewerId,
      update,
    }) => {
      const history = useHistory();
      const variables = { from: viewerId, to: userId };
      const [addLike, { loading }] = useMutation<AddLike>(ADD_LIKE, {
        notifyOnNetworkStatusChange: true,
      });
      const [addDislike, { loading: disLoading }] = useMutation<AddDislike>(
        DISLIKE,
        {
          notifyOnNetworkStatusChange: true,
        }
      );
      const dislike = async () => {
        await addDislike({ variables });
        update(userId);
      };
    
      const like = async () => {
        const result = await addLike({ variables });
        const matchId = result.data?.like?.matchId;
    
        if (matchId) {
          // go to match
          message.success(
            `Great! You matched with ${name}! Say hi, by adding your first track.`
          );
          history.push(`/matches/${matchId}`);
        }
        update(userId);
      };
    
      return (
        <Card
          hoverable
          className={"card"}
          style={{
            cursor: "auto",
            marginTop: 20,
          }}
          actions={[
            disLoading ? (
              <Spin indicator={antIcon} />
            ) : (
              <DislikeFilled
                onClick={dislike}
                style={{ fontSize: 22 }}
                key="dislike"
              />
            ),
            loading ? (
              <Spin indicator={antIcon} />
            ) : (
              <LikeFilled style={{ fontSize: 22 }} onClick={like} key="like" />
            ),
          ]}
        >
          <Meta
            avatar={<Avatar size={50} src={imageUrl || getRandomImage(name)} />}
            title={name}
            description={bio}
          />
          <p style={{ marginTop: 20, color: "rgb(150,150,150)" }}>
            <span>{score} overlapping</span>
          </p>
          <div
            style={{
              borderTop: "1px solid rgb(200,200,200)",
            }}
          >
            <h4 style={{ marginTop: 20 }}>I am skilled at</h4>
          </div>
          <Tags items={skills} />
        </Card>
      );
    };
    export default FireCard;
    


    이것이 바로 apollo의 장점입니다. 직관적이고 이해하기 쉬운 방식으로 데이터 가져오기 및 관리 요구 사항을 처리합니다. 더 이상 캐시 관리 redux 코드나 sagas 테스트를 가져오지 않아도 됩니다. Apollo는 작동하며 원격 데이터 동기화 논리를 유지 관리하고 테스트하는 부담을 덜어줍니다. 일반적인 데이터 가져오기 및 관리 설정이 아닌 애플리케이션 요구 사항에 집중하십시오!

    이를 더 자세히 설명하기 위해 응용 프로그램의 사용 사례 중 하나는 일치시킬 기본 설정을 지정할 수 있는 것입니다. 기본 설정을 선택하면 애플리케이션의 홈 화면에 새 후보자 목록이 표시됩니다.



    원래 저는 Fire 구성 요소에 useEffect 후크를 설정했고 기본 설정 업데이트에서 데이터를 다시 가져오는 논리를 가지고 있었습니다. 하지만 저는 생각했습니다. 이 사용 사례는 매우 일반적입니다. Apollo가 그것에 대해 무엇을 가지고 있습니까? 그리고 예상대로 뷰어가 기본 설정을 업데이트하면 GET_USER 쿼리를 새로 고침 목록에 추가하는 편리한 refetchQueries API가 있습니다.

    const [batchPrefer, { loading: mutationLoading }] = useMutation<Batch>(
      BATCH_PREFER,
      {
        notifyOnNetworkStatusChange: true,
        refetchQueries: [{ query: GET_USER, variables: { id: props.id } }],
        awaitRefetchQueries: true,
      }
    );
    


    이것이 GRAND 스택의 실행 중인 주제입니다. 뛰어난 기술로 일반 작업을 추상화하여 비즈니스 요구 사항에 집중할 수 있습니다. 귀중한 두뇌 주기를 상용구에 사용하지 말고 이를 neo4j 및 apollo의 유능한 엔지니어링 팀에 위임하고 아이디어를 더 빨리 전달하십시오.

    제품 구축



    이 시리즈에서는 현재 live인 GRAND 스택 애플리케이션을 구축했습니다. 원래는 기술 사양을 지정한 후 neo4j 그래프로 작동하는 graphql 서버를 2시간 만에 작동시킨 후 주말 이내로 타임박스를 설정했습니다. 그러나 결국 포기하고 빠른 프로토타이핑을 위한 훌륭한 솔루션인 ant design 으로 전환한 후 tailwindcss를 사용하여 구성 요소를 그리는 데 일주일을 보냅니다. 전반적으로, 주로 UI 문제로 인해 원래 예상보다 2주가 걸렸습니다. 이 제품을 공공장소에 짓게 된 동기는 https://www.indiehackers.com/과 그래프 기술을 배우고 싶은 저에게서 나왔습니다. 이제 사용자를 통해 이 제품의 공동 창립자를 찾고 싶습니다.

    좋은 웹페이지 즐겨찾기