timhall / svelte-apollo

Svelte integration for Apollo GraphQL
MIT License
947 stars 68 forks source link

Mutations MUST be prepared inside a component #71

Closed SamJacobs closed 3 years ago

SamJacobs commented 3 years ago

I'm guessing this has something to do with the context API, but since the mutation isn't excecuted until the prepared function is invoked, I would think it is possible to hold off on calling get_current_component until then, allowing you to extract mutation preparations to util files.

Any chance that can get reworked?

e.g. instead of

  const MUTATION = gql`
    mutation Mutation {
      ...
    }
  `;

  const doMutate = mutation(MUTATION);

doMutate({variables: {}})

could do something like


/* ./doMutate*/
import {mutation} from 'svelte-apollo'

const doMutateMutation = () =>  {
      const MUTATION = gql`
        mutation Mutation {
          ...
        }
      `;
    return mutation(MUTATION)
}

export const doMutate = (variable1, variable2) => {
    doMutateMutation({variables: {variable1, variable2}});
}

/* page.svelte*/
import doMutate from './doMutate'

<div on:click={() => doMutate(variable1, variable2)} />
Enteleform commented 3 years ago

I'm having this issue too, definitely would like to be able to organize queries, mutations, etc. into separate files.

The callback solution would be ok, but it would be preferable to maintain the current developer implementation and hide the delayed context access within svelte-apollo's internals.

SamJacobs commented 3 years ago

Solution I came up with is to just curry the mutation:

const doSomething = (svelteApolloMutation) => (variables) => svelteApolloMutation({variables})

on:click=() => doSomething(variables)

I messed around with source a little bit and it doesn't matter where in the mutate function you call getClient, for some reason the current component isn't defined so it might be a svelte issue.

timhall commented 3 years ago

I organize the mutation GraphQL into a separate file rather than a function and it works fairly well for separation. If you'd rather separate it as a function, mutation is pretty much just getting the client from svelte's context when the component is initialized and then calling client.mutate. getContext is only available during initialization, so that's most likely the cause of your get_current_component issue. Rather than currying svelteApolloMutation, you could pass the Apollo client to the mutation:

// data.ts
export const ADD_BOOK = gql`
  # ...
`

export interface AddBookOptions {
  variables: {
    // ...
  }
}

export async function addBook(client: ApolloClient, options: AddBookOptions) {
  await client.mutate(ADD_BOOK, options)
}
<script>
import { getClient } from 'svelte-apollo';
import { addBook } from './data';

const client = getClient();
</script>

<button on:click="() => addBook(client, { variables: {} })">Add Book</button>

But my preferred approach is

// data.ts
import { gql } from '@apollo/client/core'

export const ADD_BOOK = gql`
  # ...
`
<script>
import { mutation } from 'svelte-apollo';
import { ADD_BOOK } from './data'

const addBook = mutation(ADD_BOOK)
</script>

<button on:click="() => addBook({ variables: {} })">Add Book</button>
SamJacobs commented 3 years ago

thanks for weighing in @timhall!