Search code examples
reactjsgraphqlapollostack

How to determine mutation loading state with react-apollo graphql


2018 Update: Apollo Client 2.1 added a new Mutation component that adds the loading property back. See @robin-wieruch's answer below and the announcement here https://dev-blog.apollodata.com/introducing-react-apollo-2-1-c837cc23d926 Read on for the original question which now only applies to earlier versions of Apollo.


Using the current version of the graphql higher-order-component in react-apollo (v0.5.2), I don't see a documented way to inform my UI that a mutation is awaiting server response. I can see that earlier versions of the package would send a property indicating loading.

Queries still receive a loading property as documented here: http://dev.apollodata.com/react/queries.html#default-result-props

My application is also using redux, so I think one way to do it is to connect my component to redux and pass down a function property that will put my UI into a loading state. Then when rewriting my graphql mutation to a property, I can make calls to update the redux store.

Something roughly like this:

function Form({ handleSubmit, loading, handleChange, value }) {
  return (
    <form onSubmit={handleSubmit}>
      <input
        name="something"
        value={value}
        onChange={handleChange}
        disabled={loading}
      />
      <button type="submit" disabled={loading}>
        {loading ? 'Loading...' : 'Submit'}
      </button>
    </form>
  );
}

const withSubmit = graphql(
  gql`
    mutation submit($something : String) {
      submit(something : $something) {
        id
        something
      }
    }
  `, 
  {
    props: ({ ownProps, mutate }) => ({
      async handleSubmit() {
        ownProps.setLoading(true);
        try {
          const result = await mutate();
        } catch (err) {
          // @todo handle error here
        }
        ownProps.setLoading(false);
      },
    }),
  }
);

const withLoading = connect(
  (state) => ({ loading: state.loading }),
  (dispatch) => ({
    setLoading(loading) {
      dispatch(loadingAction(loading));
    },
  })
);

export default withLoading(withSubmit(Form));

I'm curious if there is a more idiomatic approach to informing the UI that the mutation is "in-flight." Thanks.


Solution

  • I have re-posted this question on github and the suggested solution was to use something like a react higher order component just as you proposed in your original question. I did a similar thing – without using redux though – as outlined in this gist.

    To cite Tom Coleman's response from the github issue:

    It doesn't really make sense to include loading state on the mutation container; if you think about it you could call the mutation twice simultaneously -- which loading state should get passed down to the child? My feeling is in general it's not nice to mix imperative (this.mutate(x, y, z)) with declarative (props) things; it leads to irresolvable inconsistencies.