React-ApolloClient 연습

Coaspe·2021년 2월 28일
0
post-thumbnail

🗂 Entire diretory structure


📁 common

📃 apollo-client.ts
📁 interfaces
    📃 post.interface.ts
📁 styles
    📃 index.ts
    📃 layout.css
    📃 typography.css

📁 components

📁 app
    📃 app.component.css
    📃 app.component.tsx
📁 header
    📃 header.component.css
    📃 header.component.tsx
📁 posts
    📁 posts-form
        📃 posts-form.component.css
        📃 posts-form.component.tsx
    📁 posts-grid
        📃 posts-grid.component.css
        📃 posts-grid.component.tsx
        📁 posts-grid-item
            📃 posts-grid-item.component.css
            📃 posts-grid-item.component.tsx

📁 hooks/posts ✔

📃 useCreatePost.ts
📃 useGetPosts.ts

pages

📁 about
    📃 about.page.css
    📃 about.page.tsx
📁 home
    📃 home.page.css
    📃 home.page.tsx

📃 index.tsx

📁 common

📃 apollo-client.ts

import {ApolloClient, InMemoryCache} from '@apollo/client';

const client = new ApolloClient({
    1️⃣ uri: 'https://graphqlzero.almansi.me/api',
    cache: new InMemoryCache()
});

export default client;

📋 Memo(📃 apollo-client.ts)

1️⃣ uri
    1. running GraphQl server that we request query
    

📁 interface -> 📃 post.interface.ts

1️⃣ export  interface Post {
    id: string,
    title: string,
    body: string
}

📋 Memo(📃post.interface.ts)

    1️⃣ interface Post
        1. interface of post

📁 components

📁 header -> 📃 header.component.tsx

import React from 'react';
import {NavLink} from 'react-router-dom';

import './header.component.css';

1️⃣ const Header: React.FC = () => {
    return(
        <header>
            <nav>
                <ul>
                    <li>
                        <NavLink to="/" >Home</NavLink>
                    </li>
                    <li>
                        <NavLink to="/about">About</NavLink>
                    </li>
                </ul>
            </nav>
        </header>
    );
}

export default Header;

📋 Memo(📃 header.component.tsx)

    1️⃣ const Header: React.FC
        1. Component that provides Home and About Link.

📁 app -> 📃 app.component.tsx

import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { ApolloProvider } from '@apollo/client';

import '../../common/styles';
import './app.component.css';

import Header from '../header/header.component';
import About from '../../pages/about/about.page';
import Home from '../../pages/home/home.page';
import client from '../../common/apollo-client';

const App : React.FC = () => {
    return(
        // ApolloProvider로 wrap 하므로써 어디에서든 사용이 가능하다.
        <ApolloProvider client={client}>
            <Router>
                <Header />
                1️⃣ <Switch>
                    2️⃣ <Route path="/about">
                        <About />
                    </Route>
                    <Route path="/">
                        <Home />
                    </Route>
                </Switch>
            </Router>
        </ApolloProvider>
    );
}

export default App;

📋 Memo(📃 app.component.tsx)

1️⃣ <Switch></Switch>
    1. <Switch> exclusively renders route.
    	=> ([링크텍스트](https://reactrouter.com/web/api/Switch))

2️⃣ Difference between <Route> and <Link>

    📌 First React Router library is made to achieve next two things
        1. State's transitions are captured on URL bar
        2. Ensure application starts from proper state through stateful URL address

    📌 <Link> component changes route state in application
        ![](https://velog.velcdn.com/images%2Faspalt85%2Fpost%2F024bc1cf-236e-436f-a72c-f8dfd3874b17%2F20210228181231.jpg)
        
        위 사진과 같은 component는 application이 /example state에 있음을 register 할 것이다.
        nutshell 안에서 Link component는 state to state(page to page)의 변화에 responsible하다.

        ![](https://velog.velcdn.com/images%2Faspalt85%2Fpost%2F3acf0e00-2af9-484d-a094-9d73c91a3d9c%2F20210228181629.jpg)
        
        반면에 Route component는 route state를 기반으로 특정 component를 display하도록 switch처럼 작동하는 데 responsible하다.

📁 posts ➡ 📁 posts-grid ➡ 📁 posts-grid-item ➡ 📃 posts-grid-item.component.tsx

import { Post } from '../../../../common/interfaces/post.interface';

const PostsGridItem:React.FC<{post : Post}> = (1️⃣ {post} : {post:Post}) => {
    return(
        <div>
            <p>{post.id}</p>
            <p>{post.body}</p>
            <p>{post.title}</p>
        </div>
    );
}

export default PostsGridItem;

📋 Memo(📃 posts-grid-item.component.tsx)

1️⃣ {post} : {post:Post}
    1. destructuring assignment와 그것의 type 선언이다.

📁 posts ➡ 📁 posts-grid ➡ posts-grid.component.tsx

import React from 'react';
import { Post } from '../../../common/interfaces/post.interface';
import PostsGridItem from './posts-grid-item/posts-grid-item.component';

1️⃣ const PostsGrid:React.FC<{posts : Post[]}> = ({posts} : {posts : Post[]}) => {
    return(
        <div className="posts-grid">
            {posts.map(post => (
                <div key={post.id}>
                    <PostsGridItem post={post}/>
                </div>
            ))}
        </div>
    );
}

export default PostsGrid;

📋 Memo(📃 posts-grid.component.tsx)

1️⃣ const PostsGrid:React.FC
    1. 모든 Post를 <div>로 생성하는 component이다.

📁 posts ➡ 📁 posts-form ➡ 📃 posts-form.component.tsx

import React from 'react';
import {useForm} from 'react-hook-form';
import { useCreatePost } from '../../../hooks/posts/useCreatePost';

interface FormData {
    title:string;
    body:string;
}
>
const PostsForm: React.FC = () => {
    1️⃣ const {register, handleSubmit} = useForm<FormData>();
    const createPost = useCreatePost();

    const onSubmit = handleSubmit(({title, body}) => {
        createPost({ variables: { input: { title : title, body : body } } });
    });

    return (
    <div className="posts-form">
        <form onSubmit={onSubmit}>
            <input type="text" name="title" ref={register} />
            <textarea name="body" ref={register} />
            <input type="submit" />
        </form>
    </div>
    );
}

export default PostsForm;

📋 Memo(📃 posts-form.component.tsx)

1️⃣ const {register, handleSubmit} = useForm<FormData>();
    1. ref로 register을 연결해서 input의 값을 subscribe 할 수 있다.
    2. handleSubmit 안에서 실행되는 함수의 parameters로 subscribe되어 있는 input들의 값이 전달된다.
    3. name attribute가 key로 작동하기 때문에 unique 해야한다.

📁 hooks/posts

📃 useGetPosts.ts

import {gql, useQuery} from '@apollo/client';
import {Post} from '../../common/interfaces/post.interface'

const GET_POSTS = gql`
     1️⃣ query GetPosts($options : PageQueryOptions!){ # query 이름은 마음대로 변경이 가능하다.
         ✔ posts(options: $options) { #
             ✔ data {                 # 임의로 변경 불가능 한 곳
                 ✔ id                 # 
                 ✔ title              # ✔ 표시들은 schema에 맞춰서 작성해야한다.
                 ✔ body               #
              }                      #
          }                          #
      }
`

export const useGetPosts = () : Post[] | undefined => {
  2️⃣ const {data} = useQuery(GET_POSTS,{
      variables:{ options: {paginate:{page:1, limit:10}}}
  });
  return data?.posts?.data;
}

📋 Memo(📃 useGetPosts.ts)

1️⃣ query GetPosts...
    ![](https://velog.velcdn.com/images%2Faspalt85%2Fpost%2F9e47d282-8537-4813-808f-dff21a444115%2Fimage.png)
2️⃣ const {data} = useQuery(GET_POSTS,{
    variables:{ options: {paginate:{page:1, limit:10}}}
    });
    ![](https://velog.velcdn.com/images%2Faspalt85%2Fpost%2Fcaeffef1-e53a-4383-9596-0be8812cf0e1%2Fimage.png)

📃 useCreatePost.ts

import {gql, useMutation} from '@apollo/client';

1️⃣ interface CreatePostInput {
  variables : {
      input : {
          title:string;
          body:string;
      }
  }
}

2️⃣ const CREATE_POST = gql`
  mutation CreatePost($input: CreatePostInput!) {
      🔰 createPost(input: $input){
          id
          title
          body
      }
  }
`

export 3️⃣ const useCreatePost = (): ((createPostInput: CreatePostInput) => any) => 
{
  const [createPost] = useMutation(CREATE_POST, {
      4️⃣ update(cache, {data: {createPost}}) {
          cache.modify({
              fields:{
                  posts(existingPosts = []){ // posts 이름 임의 변경 가능
                      const newPostRef = cache.writeFragment({
                          data : createPost,
                          fragment: gql`
                           fragment NewPost on Post { // NewPost, Post 임의변경가능
                               id,
                               title,
                               body
                           }
                          `
                      });
                      return [...existingPosts, newPostRef]
                  }
              }
          })
      }
  });
  return createPost;
}

📋 Memo(📃 useCreatePost.ts)

1️⃣ interface CreatePostInput
   1. mutation 함수로 넘겨줄 data의 interface를 정의한다.
2️⃣ const CREATE_POST
   1. GraphQL server로 보낼 mutation query이다.
3️⃣ const useCreatePost
   1. mutation function을 return하는 함수이다.
4️⃣ update(cache, {data: {createPost}})
   1.  cache object that represents the Apollo Client cache. 
       This object provides access to cache API methods like readQuery,
       writeQuery, readFragment, writeFragment and modify. 
       These methods enable you to execute GraphQL operations on the cache
       as though you're interacting with a GraphQL server.
   2. data는 result of mutation을 갖고 있다.
      여기에서 createPost는 🔰 뒤에 createPost와 같은 것이다.

📁 pages

📁about -> 📃 about.page.tsx

import React from 'react';
import PostsForm from '../../components/posts/posts-form/posts-form.component';

const About:React.FC = () => {
  return(
      <div className="about">
          <PostsForm />
      </div>
  );
}

export default About;

📁home -> 📃 home.page.tsx

import React from 'react';
import PostsGrid from '../../components/posts/posts-grid/posts-grid.component';
import {useGetPosts} from '../../hooks/posts/useGetPosts';

const Home:React.FC = () => {
  const posts = useGetPosts();
  return(
      <div className="home">
          1️⃣ <PostsGrid posts={posts || []}/>
      </div>
  );
}

export default Home;

📋 Memo(📃 home.page.tsx)

1️⃣ <PostsGrid posts={posts || []}/>
    1. posts는 posts or [] 이다.
profile
https://github.com/Coaspe

0개의 댓글