2

So, I wish to create a GraphQL mutation, in GraphCool, that increments the existing value of an integer field by a predetermined amount, passed in as a variable. For example:

mutation updateLikes ($id: ID!, $count: Int) {
  updatePosts (id: $id, likes: incrementBy $count) {
    id
    likes
  }
}

Query variable

{
  "id" : "cj0qkl04vep8k0177tky596og",
  "count": 1
}

How do I do this?

Also, I am attempting to trigger the above mentioned process from a onClick event, but am getting an 'cannot read property 'props' of null' error message, I suspect on this.props.client.query:

Photo.js

import React from 'react';
import Comments from './Comments';
import CSSTransitionGroup from 'react-addons-css-transition-group';
import { Link } from 'react-router';
import {
  gql,
  graphql,
  withApollo
} from 'react-apollo';
import ApolloClient from 'apollo-client';

class Photo extends React.Component {

  incrementQuery(currentID) {
    console.log("like of id=" + currentID+ " has been incremented by 1");

    this.props.client.query({
      query: gql`
        mutation updatePosts($id: ID!) {
          updatePosts (id: $id, likes: 1) {
            id
            likes
          }
        }  
      `,
      variables: {
        id: currentID,
      },
    });
  }

  render() {
    const { post, i } = this.props;

    return (
      <figure key={i} className="grid-figure">

        <div className='grid-photo-wrap'>
          <Link to={`/view/${post.id}`}>
            <img className='grid-photo' src={post.displaysrc} alt={post.caption} />
          </Link>

          <CSSTransitionGroup transitionName="like" transitionEnterTimeout={500} transitionLeaveTimeout={500}>
            <span key={post.likes} className="likes-heart">{post.likes}</span>
          </CSSTransitionGroup>

        </div>

        <figcaption>
          <p>{post.caption}</p>

          <div className="control-buttons">
            <button onClick={this.incrementQuery.bind(null,i)} className="likes">&hearts; {post.likes}</button>

            <Link to={`/view/${post.id}`} className="button">
              <span className="comment-count">
                <span className="speech-bubble"></span> {(post.comments ? Object.keys(post.comments).length : 0)}
              </span>
            </Link>
          </div>

        </figcaption>

      </figure>
    )
  }
};

Photo.PropTypes = {
  client: React.PropTypes.instanceOf(ApolloClient).isRequired,
  query: React.PropTypes.object,
  variables: React.PropTypes.object,
  data: React.PropTypes.shape({
    loading: React.PropTypes.bool.isRequired,
    error: React.PropTypes.bool.isRequired,
    updatePosts: React.PropTypes.object,
  }).isRequired,
}

const PhotoWithApollo = withApollo(Photo);

export default PhotoWithApollo;

The thrown error is as follows:

Uncaught TypeError: Cannot read property 'props' of null
    at incrementQuery (http://localhost:7770/static/bundle.js:56347:12)
    at proxiedMethod (http://localhost:7770/static/bundle.js:54898:31)
    at HTMLUnknownElement.wrapped (http://localhost:7770/static/bundle.js:47347:30)
    at Object.ReactErrorUtils.invokeGuardedCallback (http://localhost:7770/static/bundle.js:6346:17)
    at executeDispatch (http://localhost:7770/static/bundle.js:6146:22)
    at Object.executeDispatchesInOrder (http://localhost:7770/static/bundle.js:6169:6)
    at executeDispatchesAndRelease (http://localhost:7770/static/bundle.js:5599:23)
    at executeDispatchesAndReleaseTopLevel (http://localhost:7770/static/bundle.js:5610:11)
    at Array.forEach (native)
    at forEachAccumulated (http://localhost:7770/static/bundle.js:6446:10)
    at Object.processEventQueue (http://localhost:7770/static/bundle.js:5815:8)
    at runEventQueueInBatch (http://localhost:7770/static/bundle.js:6475:19)
    at Object.handleTopLevel [as _handleTopLevel] (http://localhost:7770/static/bundle.js:6491:6)
    at handleTopLevelWithoutPath (http://localhost:7770/static/bundle.js:16697:25)
    at handleTopLevelImpl (http://localhost:7770/static/bundle.js:16677:4)
    at ReactDefaultBatchingStrategyTransaction.perform (http://localhost:7770/static/bundle.js:8643:21)
    at Object.batchedUpdates (http://localhost:7770/static/bundle.js:12680:20)
    at Object.batchedUpdates (http://localhost:7770/static/bundle.js:8148:21)
    at dispatchEvent (http://localhost:7770/static/bundle.js:16808:21)
    at HTMLDocument.wrapped (http://localhost:7770/static/bundle.js:47347:30)
TheoG
  • 1,498
  • 4
  • 30
  • 54

3 Answers3

3

Currently you need to first query the value:

query Post($id: ID!) {
  Post (id: $id) {
    id
    likes
  }
}

Then increment the likes client-side and update the post with the new value for likes:

mutation updateLikes ($id: ID!, $likes: Int!) {
  updatePost (id: $id, likes: $likes) {
    id
    likes
  }
}

Incrementing the likes by a special number is a great use case for a custom mutation, which we are already working on.

marktani
  • 7,578
  • 6
  • 37
  • 60
  • Many thanks for your response. I am actually trying to trigger this process from an onClick event, please see my updated question, but am getting an empty props error. What am I overlooking, and is this the best way to do this? – TheoG Mar 29 '17 at 15:12
  • Where are you setting the props of the component? And where is the error thrown? – marktani Mar 29 '17 at 16:01
  • The thrown error is being generated against 'this.props.client.query({', to which I have specified Photo.PropTypes at the bottom of Photo,js, above the error message i have just listed. – TheoG Mar 29 '17 at 16:35
  • looks like `this` is not bound to `incrementQuery` correctly – marktani Mar 30 '17 at 08:34
  • On further consideration, the only issue with the current solution is the interval between retrieving the current likes snapshot, updating it on the client, and then updating Posts with the new value, in which other users could have already updated 'their' current likes snapshot, thus making what likes is currently being updated with, totally incorrect. Is there an eta for custom queries/mutations? – TheoG Apr 05 '17 at 10:01
0

Yes. This is a very interesting question. Actually, Apollo client use same method to handle pagination. you must be get current page,when you go to bottom of page, (prev page+1)=>(next page) be a new page variable to get next page content ,then concat to prev page 。 dislike/like,the same way , only difference does't need to concat to prev dike/like. replace it.

Zhang Yung
  • 46
  • 1
0

For the AWS users, I personnaly had the same issue, for an accounting system. The risk with reading and then updating the value is that if several users call your script, some issues of concurrencies might happen. (A user can read the curent value just before it's being updated by another user, so he will arase this other user incrementation). What I did is :

  1. Create a queue request table where I explicitly write that I want to increment my variable.
  2. Create a lambda that is triggered on that table events (this lambda will fire anytime something happens in this table)
  3. Limit this lambda concurrency to 1 to avoid reading a value that is being changed
  4. Read the value from the request queue table, increment my final element and remove the request queue from the queue table. All this has to be done in the unique concurrency lambda that you just created.
vince
  • 1
  • 1