Skip to content

Instantly share code, notes, and snippets.

@wuweiweiwu
Last active October 10, 2022 21:06
Show Gist options
  • Save wuweiweiwu/a4bcfc86133a4684e3c531540ff68446 to your computer and use it in GitHub Desktop.
Save wuweiweiwu/a4bcfc86133a4684e3c531540ff68446 to your computer and use it in GitHub Desktop.
// given a commit function from useMutation
// return a promisfied version of the commit function that resolves to the mutation data
// and rejects with the mutation error
export function promisfy<TMutation extends MutationParameters>(commit: (config: UseMutationConfig<TMutation>) => Disposable) {
return (config: UseMutationConfig<TMutation>) => {
return new Promise<TMutation['response']>((resolve, reject) => {
// override the onComplete and onError callbacks to promisfy commit
const newConfig: UseMutationConfig<TMutation> = {
...config,
onCompleted: (data, errors) => {
config.onCompleted?.(data, errors);
resolve(data);
},
onError: (error) => {
config.onError?.(error);
reject(error);
},
};
commit(newConfig);
});
};
}
export type DeepPartial<T> = {
[P in keyof T]?: DeepPartial<T[P]>;
};
import * as React from 'react';
import { GraphQLTaggedNode, applyOptimisticMutation } from 'react-relay';
import { UseMutationConfig, useRelayEnvironment, useMutation as useRelayMutation } from 'react-relay/hooks';
import { MutationParameters } from 'relay-runtime';
import merge from 'deepmerge';
import { promisfy } from './promisfy';
import { DeepPartial } from './types';
// similar to useMutation but returns a start function
// start immediately applies to optimistic update and returns an object with 2 values
// - undo reverts the optimistic update
// - commit sends the network request and returns a promise that resolves with the response or rejects with error
export function useUndoableMutation<TMutation extends MutationParameters>(
mutation: GraphQLTaggedNode
): (
config: UseMutationConfig<TMutation>
) => {
undo: () => void;
commit: (configOverride?: DeepPartial<UseMutationConfig<TMutation>>) => Promise<TMutation['response']>;
} {
const environment = useRelayEnvironment();
const [relayCommit] = useRelayMutation<TMutation>(mutation);
const start = React.useCallback(
(config: UseMutationConfig<TMutation>) => {
// apply optimistic mutation using the same config as to the actual mutation
const disposable = applyOptimisticMutation(environment, { ...config, mutation });
return {
undo: () => disposable.dispose(),
commit: (configOverride?: DeepPartial<UseMutationConfig<TMutation>>) => {
disposable.dispose();
const promisfiedCommit = promisfy(relayCommit);
return promisfiedCommit(merge(config, configOverride || {}));
},
};
},
[environment, mutation, relayCommit]
);
return start;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment