jaydenseric / graphql-react

A GraphQL client for React using modern context and hooks APIs that is lightweight (< 4 kB) but powerful; the first Relay and Apollo alternative with server side rendering.
https://npm.im/graphql-react
MIT License
717 stars 22 forks source link

Mutation example #36

Closed alex77g closed 5 years ago

alex77g commented 5 years ago

hi, i struggled a little bit with the mutations. at last i made it work. If someone has the same question here is a short example:

import {Query} from "graphql-react/lib/components";

export default () => (
    <Query
        loadOnMount
        loadOnReset
        fetchOptionsOverride={options => {
            options.url = 'http://localhost:8080/query'
        }}
        operation={{
            query: /* GraphQL */ `
              mutation {
                createAddition(input: { number: 22, anotherNumber: 33 }) {
                  result
                }
              }
            `
        }}
    >
        {({ data, loading }) =>
            data ? (
                <div>{data.createAddition.result}</div>
            ) : loading ? (
                <p>Loading…</p>
            ) : (
                <p>Error!</p>
            )
        }
    </Query>
)

@jaydenseric maybe you can use it for the docs

jaydenseric commented 5 years ago

I wrote a big reply but then I closed my browser before submitting 😭

In the current API, <Query> no longer exists; you should be using React hooks.

Also, with the old API the import was:

- import {Query} from "graphql-react/lib/components";
+ import {Query} from "graphql-react";

I find your example very confusing, honestly it's not very realistic. Why would you want the mutation to load on mount?

You might find this cache related options guide handy, for choosing when to use loadOnMount, loadOnReload, loadOnReset, reloadOnLoad, and resetOnLoad.

For your benefit, here is an example of how queries and mutations should be composed:

import { useGraphQL } from 'graphql-react'

const fetchOptionsOverride = options => {
  options.url = 'http://localhost:8080/query'
}

export const PostLikeButton = ({ postId, liked }) => {
  const { load, loading } = useGraphQL({
    loadOnMount: false,
    loadOnReload: false,
    loadOnReset: false,
    reloadOnLoad: true,
    fetchOptionsOverride,
    operation: {
      variables: { postId, liked: !liked },
      query: /* GraphQL */ `
        mutation($postId: ID!, $liked: Boolean!) {
          postEditLike(postId: $postId, liked: $liked) {
            success
          }
        }
      `
    }
  })

  return (
    <button onClick={load} disabled={loading}>
      ❤️{liked ? ' ✔️' : ''}
    </button>
  )
}

export const Post = ({ postId }) => {
  const { loading, cacheValue: { data } = {} } = useGraphQL({
    fetchOptionsOverride,
    operation: {
      variables: { postId },
      query: /* GraphQL */ `
        query($postId: ID!) {
          post: node(id: $postId) {
            ... on Post {
              title
              content
              liked
            }
          }
        }
      `
    }
  })

  return data ? (
    <article>
      <h1>{data.post.title}</h1>
      <p>{data.post.content}</p>
      <PostLikeButton postId={postId} liked={data.post.liked} />
    </article>
  ) : loading ? (
    'Loading…'
  ) : (
    'Loading failed!'
  )
}

Note that the cache related useGraphQL hook options default to making queries easier instead of mutations. That is because mutations happen a lot less often.

Also note that the parent <Post> component is responsible for populating the liked status of the button. Ideally, the only places you have a query are:

This prevents loading waterfalls and reduces requests to your GraphQL API. On a fresh page load the header, footer, and page content can all load in parallel and there is no nested loading of page components.