Skip to content

hchiam/learning-react-apollo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Learning React + Apollo (+ GraphQL)

Just one of the things I'm learning. https://github.com/hchiam/learning

Example: https://github.com/hchiam/learning-react-apollo#hasura---heroku---codesandbox---live-demo-httpsvw9i2csbapp

My notes based off of https://codeartistry.io/the-react-apollo-2020-cheatsheet

More on React: https://github.com/hchiam/learning-reactjs

Apollo vs Axios:

  • Apollo = GraphQL API
  • Axios = REST API

Big picture summary:

Apollo = "frontend+backend connector" = "FE+BE connector"

Apollo gives us custom hooks to do this:

React (UI, FE) <-> Apollo <-> GraphQL (database + files, BE)

The core Apollo hooks:

  • useQuery = "get x1"
  • useLazyQuery = "await get x1"
  • useMutation = "change"
  • useSubscription = "get whenever updates"

You can use Apollo hooks to:

  • manually set the fetchPolicy option to "network-first", to override old Apollo local cache

  • "manually" update the local cache query upon mutation, to override old Apollo local cache

  • refetch queries with useQuery's refetch when useMutation onCompleted

    • (like when, after deleting some DB data in the BE, you want to update the UI in the FE)
  • refetch queries with useMutation's refetchQueries option

  • access the Apollo client with useApolloClient or new ApolloClient (both give you a promise that you need to .then and .catch)

Apollo setup: (with yarn)

If you only need to "manually" trigger queries and mutations in your app:

yarn add @apollo/react-hooks apollo-boost graphql
import ApolloClient from "apollo-boost";

const client = new ApolloClient({
  uri: "https://your-graphql-endpoint.com/api/graphql",
});

Or if you also need subscriptions to "automatically" trigger queries and mutations in your app:

yarn add @apollo/react-hooks apollo-client graphql graphql-tag apollo-cache-inmemory apollo-link-ws
import ApolloClient from "apollo-client";
import { WebSocketLink } from "apollo-link-ws";
import { InMemoryCache } from "apollo-cache-inmemory";

const client = new ApolloClient({
  link: new WebSocketLink({
    url: "https://your-graphql-endpoint.com/api/graphql",
    options: {
      reconnect: true,
      connectionParams: {
        headers: {
          Authorization: "Bearer yourauthtoken",
        },
      },
    },
  }),
  cache: new InMemoryCache(),
});
Apollo client (and some `gql` stuff)

Apollo client

You can directly use the Apollo client, but it gives you a promise that you have to .then and .catch:

import { ApolloProvider } from "@apollo/react-hooks";
// ...
return <ApolloProvider client={client}>{/* ... */}</ApolloProvider>;
// import GET_POST, GET_POSTS, CREATE_POST

client
  .query({ query: GET_POSTS, variables: { limit: 5 } })
  .then((res) => console.log(res.data))
  .catch((err) => console.error(err));

client
  .mutate({ mutation: CREATE_POST, variables: { title: "...", body: "..." } })
  .then((res) => console.log(res.data))
  .catch((err) => console.error(err));

client
  .subscribe({ subscription: GET_POST, variables: { id: "..." } })
  .then((res) => console.log(res.data))
  .catch((err) => console.error(err));
import { gql } from "apollo-boost";
// or
import gql from "graphql-tag";

const GET_POSTS = gql`
  query GetPosts($limit: Int) {
    posts(limit: $limit) {
      id
      body
      title
      createdAt
    }
  }
`;

const CREATE_POST = gql`
  mutation CreatePost($title: String!, $body: String!) {
    insert_posts(objects: { title: $title, body: $body }) {
      affected_rows
    }
  }
`;

const GET_POST = gql`
  subscription GetPost($id: uuid!) {
    posts(where: { id: { _eq: $id } }) {
      id
      body
      title
      createdAt
    }
  }
`;

// btw, "!" means non-nullable/required; GraphQL types are nullable by default.

There's also:

client.resetStore().then(console.log("...")); // resetStore() is async!
client.readData();
client.writeData();
// and more!

Apollo convenience hooks:

useQuery

useQuery = "get x1"

const { loading, error, data } = useQuery(GET_POSTS, {
  variables: { limit: 5 },
});
// loading: boolean
// error: boolean
// data: data
// ...: (other destructured variables)

if (loading) return <div>Loading...</div>;
if (error) return <div>Error...</div>;
return data.posts.map((post) => <div key={post.id}>post.text</div>);
useLazyQuery

useLazyQuery = "await get x1"

const [searchPosts, { data }] = useLazyQuery(SEARCH_POSTS, {
  variables: { query: `%${query}%` },
});
// (first item in array): query function
// {...}: object that contains { loading, error, data, called, ... }
useEffect(() => {
  // ...
  searchPosts();
  if (data) setResults(data.posts);
}, [query, data, searchPosts]);
if (called && loading) return <div>Loading...</div>;
return results.map((result) => <div key={result.id}>result.text</div>);
useMutation

useMutation = "change"

import { useMutation } from '@apollo/react-hooks'; and then:

const [createPost, { loading, error, data }] = useMutation(CREATE_POST);
// (first item in array): mutation function
// {...}: object that contains { loading, error, data, ... }
// you can use "loading" variable to debounce

or

const [createPost, { loading, error, data }] = useMutation(CREATE_POST, {
  onCompleted: (data) => console.log(data),
  onError: (error) => console.error(error),
});
useSubscription

useSubscription = "get whenever updates"

import { useSubscription } from '@apollo/react-hooks'; and then:

const [createPost, { loading, error, data }] = useSubscription(GET_POST, {
  variables: { id },
  shouldResubscribe: true, // run query GET_POST when props change (default is false)
  onSubscriptionData: (data) => console.log("new data", data), // when subscription hook gets new data
  fetchPolicy: "network-only", // (default is 'cache-first')
});

Overriding the local Apollo cache when it's old:

(The Apollo cache is only on the client side.)

Set fetchPolicy option to "network-first"
const { loading, error, data } = useQuery(GET_POSTS, {
  variables: { limit: 5 },
  fetchPolicy: "network-first", // instead of the default 'cache-first'
});

Options for fetchPolicy:

  • cache-and-network
  • cache-first (default)
  • cache-only
  • network-only
  • no-cache
  • standby
Update local cache query upon mutation

Notice cache.writeQuery inside the update option inside useMutation:

function EditPost({ id }) {
  const [updatePost] = useMutation(UPDATE_POST, {
    update: (cache, data) => {
      const { posts } = cache.readQuery(GET_POSTS);
      const newPost = data.update_posts.returning;

      // old posts -> new posts:
      const updatedPosts = posts.map((post) =>
        post.id === id ? newPost : post
      );

      // update local cache query:
      cache.writeQuery({ query: GET_POSTS, data: { posts: updatedPosts } });
    },
    onCompleted: () => history.push("/"),
  });
}

Refetch queries:

Refetch queries with useQuery's refetch when useMutation onCompleted

(like when, after deleting some DB data in the BE, you want to update the UI in the FE)

The following code basically says "re-get data when complete change":

const { loading, data, refetch } = useQuery(GET_POSTS); // useQuery = "get"
// ...
const [deletePost] = useMutation(DELETE_POST, {
  // mutation = "change"
  onCompleted: () => refetch(), // "change complete"
});
Refetch queries with useMutation's refetchQueries option
const arrayOfQueriesToRefetchAfterwards = [
  { query: GET_POSTS, variables: { limit: 5 } },
];
const [createPost] = useMutation(CREATE_POST, {
  refetchQueries: arrayOfQueriesToRefetchAfterwards,
});
Accessing/using the Apollo client with `useApolloClient`
const client = useApolloClient(); // same as new ApolloClient();

client()
  .resetStore() // note: resetStore is async!
  .then(() => console.log("..."));

Hasura -> Heroku -> Codesandbox -> live demo: https://vw9i2.csb.app

  1. https://hasura.io/docs/1.0/graphql/core/deployment/deployment-guides/heroku.html -> "Deploy to Heroku"

  2. https://react-graphql1-2.herokuapp.com/console to run/try GraphQL queries

  3. -> create something like this: https://codesandbox.io/s/github/hchiam/react-apollo-gql-example

(Backed up on GitHub at: https://github.com/hchiam/react-apollo-gql-example)

(More info: https://codeartistry.io/the-react-graphql-2020-crash-course)

Example uses:

index.js --> App.js --> things like Post.js, to

const DELETE_POST = gql`
  ...
`;

https://github.com/hchiam/react-apollo-gql-example/blob/main/src/index.js

https://github.com/hchiam/react-apollo-gql-example/blob/main/src/App.js

https://github.com/hchiam/react-apollo-gql-example/blob/main/src/components/Post.js

https://github.com/hchiam/react-apollo-gql-example/blob/main/src/components/NewPost.js

About

Learning React + Apollo + GraphQL

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published