Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimistic Response with debounce mutations #14

Open
jaril5976 opened this issue Oct 26, 2020 · 3 comments
Open

Optimistic Response with debounce mutations #14

jaril5976 opened this issue Oct 26, 2020 · 3 comments

Comments

@jaril5976
Copy link

jaril5976 commented Oct 26, 2020

  • Hello I am updating user input with the real-time optimistic response, I had applied to debounce link for 1 second, optimistic works fine and also debounce but that will freeze the browser, if I remove the debounce it will work fine with zero lags, but I want to stop backend call while user typing so I have to use this debounce-link, any help would be appreciated

code example:

This is optimistic query

return {
    __typename: 'Mutation',
    updateBlockItemField: {
      __typename: 'Resume',
      ...resume,
      id: resumeId,
      updatesCount: resume.updatesCount + 1,
      details: {
        ___typename: 'ResumeDetails',
        ...resume.details,
      },
      settings: {
        ___typename: 'ResumeSettings',
        ...resume.settings,
      },
      blocks: resume.blocks,
    },
  };

This is Mutation:

save = debounce(value => {
    const { mutate, variables, optimisticResponse, update, isCoverLetter } = this.props;
    const options = {
      variables: {
        ...variables,
        value,
      },
      context: {
        debounceKey: generateDebounceKey(variables),
      },
    }

    if (optimisticResponse) options.optimisticResponse = optimisticResponse(value);
    mutate(options);
  }, 30);

And this is my init

return new ApolloClient({
    connectToDevTools: isBrowser,
    ssrMode: !isBrowser, // Disables forceFetch on the server (so queries are only run once)
    link: ApolloLink.from([
      onError(({ graphQLErrors, networkError }) => {
        if (graphQLErrors) {
          graphQLErrors.map(({ message, locations, path }) =>
            console.error(`[GraphQL error]: Message: ${message}, Path: ${path}`),
          );
        }

        if (networkError) {
          console.error(`[${networkError.name}]: Status: ${networkError.statusCode}: Message: ${networkError.message}`);
        }
      }),
      new DebounceLink(1000),
      authLink.concat(
        ApolloLink.split(
          operation => operation.getContext().client === 'coverLetter', // Routes the query to the proper client
          coverLetterLink,
          httpLink,
        ),
      ),
    ]),
    cache,
    resolvers: {},
  });
@PeterDekkers
Copy link

PeterDekkers commented Nov 11, 2021

I managed to solve this same problem by using custom debouncing, without using apollo-link-debounce.

In a nutshell, for the mutations that I want to debounce, I call a wrapper function which:

  1. Immediately writes the optimistic data to the Apollo cache (using writeFragment). This is an alternative to using optimisticResponse and updates the UI instantly.
  2. Calls a debounced mutation (without optimisticResponse, as the data is already written to the cache).

@edgars-sirokovs
Copy link

@PeterDekkers could you share your implementation?

@PeterDekkers
Copy link

PeterDekkers commented Jun 6, 2023

@edgars-sirokovs, something like this:

import React from 'react';
import debounce from 'lodash/debounce';
import { useApolloClient, useMutation } from '@apollo/client';

const UPDATE_DEBOUNCE_TIME = 1500;

const mutationQuery = gql`
mutation Blah($input: Input!) {
    updateQuery(input: $input) {
        yourQueryHere
    }
}
`;

function useDebouncedMutation() {
    const [doUpdate] = useMutation(mutationQuery);
    const client = useApolloClient();

    const debouncedUpdate = React.useMemo(
        () =>
            debounce(doUpdate, UPDATE_DEBOUNCE_TIME, {
                // Send one immediately
                leading: true,
            }),
        [doUpdate],
    );

    return async ({
        mutationInput
    }) => {
        // Replace this with your way to retrieve the optimistic result
        const optimisticResult = getOptimisticResult(mutationInput);

        const queryVariables = {
            input: mutationInput
        };

        const queryData = {
            doUpdate: {
                __typename: 'UpdateMutation',
                foo: bar,
            },
        };

        // Write to cache for instant UI update.
        // An alternative to `optimisticResponse`, as the mutations are debounced.
        client.writeQuery({
            query: mutationQuery,
            variables: queryVariables,
            data: queryData,
            overwrite: true,
        });

        return debouncedCartUpdate({
            variables: queryVariables,
            ignoreResults: true,
            // Don't write to cache again, we've done that already above.
            // Furthermore, writing to cache here causes a performance
            // issue, where `writeQuery` doesn't immediately update cache
            // when requests are in flight, making the UI seem sluggish.
            // This does mean that the optimistic cart response is taken
            // for truth.
            fetchPolicy: 'no-cache',
        });
    };
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants