2

I have the mutation below in a React Component. Im going to need the same mutation in multiple components and on different pages.

How can I reuse my mutation code without repeating it?

This example isn't that complex but some queries use optimistic UI and write to the store.

import React from 'react';
import { graphql, compose } from 'react-apollo';
import { gql } from 'apollo-boost';

const JoinLocation = props => {
    if (props.ME.loading) return null;
    const { locationMachineName } = props;
    const me = props.ME.me;
    const submit = () => {
        props
            .JOIN_LOCATION({
                variables: {
                    userId: me.id,
                    locationMachine: locationMachineName,
                },
            })
            .catch(err => {
                console.error(err);
            });
    };
    return <button onClick={() => submit()}>Join location</button>;
};

const ME = gql`
    query me {
        me {
            id
        }
    }
`;

const JOIN_LOCATION = gql`
    mutation joinLocation($userId: ID!, $locationId: ID!) {
        joinLocation(userId: $userId, locationId: $locationId) {
            id
        }
    }
`;

export default compose(
    graphql(JOIN_LOCATION, { name: 'JOIN_LOCATION' }),
    graphql(ME, { name: 'ME' }),
)(JoinLocation);
Evanss
  • 23,390
  • 94
  • 282
  • 505

1 Answers1

1

Create a higher-order component (HOC) for the mutation/query that contains the gql options and optimistic UI logic:

const JOIN_LOCATION = gql`
    mutation joinLocation($userId: ID!, $locationId: ID!) {
        joinLocation(userId: $userId, locationId: $locationId) {
            id
        }
    }
`;

export const withJoinLocation = component => graphql(JOIN_LOCATION, { name: 'JOIN_LOCATION' })(component);

Then wrap your different components with it.

export default withJoinLocation(JoinLocation);

UPDATE: Based on your below comment, if you want to encapsulate the whole submit logic and not just the mutation as stated in your question, you can use a render prop like so:

import React from 'react';
import { graphql, compose } from 'react-apollo';
import { gql } from 'apollo-boost';

const JoinLocation = props => {
    if (props.ME.loading) return null;
    const { locationMachineName } = props;
    const me = props.ME.me;
    const submit = () => {
        props
            .JOIN_LOCATION({
                variables: {
                    userId: me.id,
                    locationMachine: locationMachineName,
                },
            })
            .catch(err => {
                console.error(err);
            });
    };
    return props.render(submit);
};

const ME = gql`
    query me {
        me {
            id
        }
    }
`;

const JOIN_LOCATION = gql`
    mutation joinLocation($userId: ID!, $locationId: ID!) {
        joinLocation(userId: $userId, locationId: $locationId) {
            id
        }
    }
`;

export default compose(
    graphql(JOIN_LOCATION, { name: 'JOIN_LOCATION' }),
    graphql(ME, { name: 'ME' }),
)(JoinLocation);

Now any component can consume the reusable submit logic. Assume you name the above component JoinLocation.js:

import JoinLocation from './JoinLocation';

const Comp = () => {
  return <JoinLocation render={submit => <button onClick={() => submit()}>Join location</button>}/>
}
Rami Enbashi
  • 3,526
  • 1
  • 19
  • 21
  • I need to encapsulate the submit function not just the gql string. Can this be done with a HOC? I havn't actually tried a HOC but I couldn't get it working with a render prop. – Evanss May 07 '18 at 15:14
  • Im getting an error. In JoinLocation.js "return this.props.render(submit);" TypeError: Cannot read property 'render' of undefined – Evanss May 08 '18 at 03:27