Skip to content

Commit

Permalink
optimistic response
Browse files Browse the repository at this point in the history
Migrating changes over from PR #321
  • Loading branch information
davidwoody authored and Sashko Stubailo committed Jul 1, 2016
1 parent 741b9d1 commit 51bc919
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Expect active development and potentially significant breaking changes in the `0
- Deprecate `apollo-client/gql` for `graphql-tag` and show a meaningful warning when importing
`apollo-client/gql`

- Allow `client.mutate` to accept an `optimisticResponse` argument to update the cache immediately, then after the server responds replace the `optimisticResponse` with the real response.

### v0.3.22 + v0.3.23 + v0.3.24

- Fix unintentional breaking change where `apollo-client/gql` import stopped working. [Issue #327](https://github.com/apollostack/apollo-client/issues/327)
Expand Down
17 changes: 16 additions & 1 deletion src/QueryManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
import forOwn = require('lodash.forown');
import assign = require('lodash.assign');
import isEqual = require('lodash.isequal');
// import merge = require('lodash.merge');

import {
ApolloStore,
Expand All @@ -33,6 +34,10 @@ import {
applyTransformerToOperation,
} from './queries/queryTransform';

import {
NormalizedCache,
} from './data/store';

import {
GraphQLResult,
Document,
Expand Down Expand Up @@ -202,11 +207,13 @@ export class QueryManager {
variables,
resultBehaviors,
fragments = [],
optimisticResponse,
}: {
mutation: Document,
variables?: Object,
resultBehaviors?: MutationBehavior[],
fragments?: FragmentDefinition[],
optimisticResponse?: Object,
}): Promise<GraphQLResult> {
const mutationId = this.generateQueryId();

Expand Down Expand Up @@ -240,6 +247,7 @@ export class QueryManager {
variables,
mutationId,
fragmentMap: queryFragmentMap,
optimisticResponse,
});

return this.networkInterface.query(request)
Expand All @@ -249,6 +257,7 @@ export class QueryManager {
result,
mutationId,
resultBehaviors,
optimisticResponse,
});

return result;
Expand Down Expand Up @@ -372,8 +381,9 @@ export class QueryManager {
queryStoreValue.networkError.stack);
}
} else {

const resultFromStore = readSelectionSetFromStore({
store: this.getApolloState().data,
store: this.getApolloCacheData(),
rootId: queryStoreValue.query.id,
selectionSet: queryStoreValue.query.selectionSet,
variables: queryStoreValue.variables,
Expand Down Expand Up @@ -440,6 +450,11 @@ export class QueryManager {
return this.store.getState()[this.reduxRootKey];
}

public getApolloCacheData(): NormalizedCache {
const state = this.getApolloState();
return assign({}, state.data, state.optimistic.data) as NormalizedCache;
}

public addQueryListener(queryId: string, listener: QueryListener) {
this.queryListeners[queryId] = listener;
};
Expand Down
1 change: 1 addition & 0 deletions src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export interface MutationInitAction {
variables: Object;
mutationId: string;
fragmentMap: FragmentMap;
optimisticResponse: Object;
}

export function isMutationInitAction(action: ApolloAction): action is MutationInitAction {
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,10 @@ export default class ApolloClient {

public mutate = (options: {
mutation: Document,
resultBehaviors?: MutationBehavior[],
variables?: Object,
resultBehaviors?: MutationBehavior[],
fragments?: FragmentDefinition[],
optimisticResponse?: Object,
}): Promise<GraphQLResult> => {
this.initStore();
return this.queryManager.mutate(options);
Expand Down
49 changes: 49 additions & 0 deletions src/mutations/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,52 @@ export function mutations(

return previousState;
}

export interface OptimisticStore {
data: Object;
mutationIds: Object;
}

const optimisticDefaultState = {
data: {},
mutationIds: {},
};

export function optimistic(
previousState = optimisticDefaultState,
action,
config
): OptimisticStore {
if (isMutationInitAction(action) && action.optimisticResponse) {
const newState = {
data: assign({}, previousState.data),
mutationIds: assign({}, previousState.mutationIds),
};
const { dataIdFromObject } = config;
const dataId = dataIdFromObject(action.optimisticResponse);

newState.data[dataId] = action.optimisticResponse;
newState.mutationIds[dataId] = action.mutationId;

return newState;
} else if (isMutationResultAction(action) && action.optimisticResponse) {
let newState = previousState;
const { dataIdFromObject } = config;
const dataId = dataIdFromObject(action.optimisticResponse);
const lastMutationId = newState.mutationIds[dataId];

if (lastMutationId === action.mutationId) {
newState = {
data: assign({}, previousState.data),
mutationIds: assign({}, previousState.mutationIds),
};
delete newState.data[dataId];
delete newState.mutationIds[dataId];
}

return newState;
}

return previousState;
}

4 changes: 4 additions & 0 deletions src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
import {
mutations,
MutationStore,
optimistic,
OptimisticStore,
} from './mutations/store';

import {
Expand All @@ -36,6 +38,7 @@ export interface Store {
data: NormalizedCache;
queries: QueryStore;
mutations: MutationStore;
optimistic: OptimisticStore;
}

// This is our interface on top of Redux to get types in our actions
Expand Down Expand Up @@ -66,6 +69,7 @@ export function createApolloReducer(config: ApolloReducerConfig): Function {
// Note that we are passing the queries into this, because it reads them to associate
// the query ID in the result with the actual query
data: data(state.data, action, state.queries, state.mutations, config),
optimistic: optimistic(state.optimistic, action, config),
};

return newState;
Expand Down
12 changes: 12 additions & 0 deletions test/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ describe('client', () => {
queries: {},
mutations: {},
data: {},
optimistic: {
data: {},
mutationIds: {},
},
},
}
);
Expand All @@ -168,6 +172,10 @@ describe('client', () => {
queries: {},
mutations: {},
data: {},
optimistic: {
data: {},
mutationIds: {},
},
},
}
);
Expand Down Expand Up @@ -354,6 +362,10 @@ describe('client', () => {
'allPeople({"first":1})': 'ROOT_QUERY.allPeople({"first":1})',
},
},
optimistic: {
data: {},
mutationIds: {},
},
},
};

Expand Down
12 changes: 12 additions & 0 deletions test/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ describe('createApolloStore', () => {
queries: {},
mutations: {},
data: {},
optimistic: {
data: {},
mutationIds: {},
},
}
);
});
Expand All @@ -34,6 +38,10 @@ describe('createApolloStore', () => {
queries: {},
mutations: {},
data: {},
optimistic: {
data: {},
mutationIds: {},
},
}
);
});
Expand All @@ -48,6 +56,10 @@ describe('createApolloStore', () => {
data: {
'test.0': true,
},
optimistic: {
data: {},
mutationIds: {},
},
},
};

Expand Down

0 comments on commit 51bc919

Please sign in to comment.