awslabs / aws-mobile-appsync-sdk-js

JavaScript library files for Offline, Sync, Sigv4. includes support for React Native
Apache License 2.0
921 stars 266 forks source link

Query is empty after 2nd mutation update with optimisticResponse #567

Open roballsopp opened 4 years ago

roballsopp commented 4 years ago

Do you want to request a feature or report a bug? Report a bug

I'm trying to implement an optimistic response and cache update for one of my queries in my react native app so I can support offline use, but I'm running into an issue when using this workflow while the device still has service. I can see my mutation updater fire twice, as expected. The first time it fires it puts the optimistic response into the cache and the query component with the query I am updating re-renders correctly, as far as I can tell. The second time the updater fires (to update the cache with the actual server response) the part of the query I am updating comes out of the query component as null even though I have observed inside my updater the response from the server is identical to my optimistic response. I even find the query correctly after the update by querying the cache for it again inside the updater (see below).

This issue does not occur when I use import { ApolloClient } from 'apollo-client'; (I didn't install a different version, its the 2.4.6 version brought in by appsync). It only occurs when using import AWSAppSyncClient from 'aws-appsync';

Here's my code:

// SurveyQueryContainer.js
import React from 'react';
import PropTypes from 'prop-types';
import { Query } from 'react-apollo';
import SurveyNavigator from './SurveyNavigator';
import { SurveyQueryContainerQuery } from './graphql/SurveyQueryContainer';

export default function SurveyQueryContainer({ route, navigation }) {
    const { surveyTemplateId, surveyResponseId } = route.params;

    return (
        <Query
            variables={{ surveyTemplateId, surveyResponseId }}
            query={SurveyQueryContainerQuery}>
            {({ loading, error, data, refetch }) => {
                                // data.getSurveyResponse is null after the 2nd mutation updater call
                return <SurveyNavigator surveyTemplate={data.getSurveyTemplate} surveyResponse={data.getSurveyResponse} />;
            }}
        </Query>
    );
}
// SurveyQueryContainer.js
import gql from 'graphql-tag';
import { SurveyNavigatorFragments } from '../SurveyNavigator';

export const surveyTemplateFragment = gql`
    fragment SurveyQueryContainer_surveyTemplate on SurveyTemplate {
        id
        name
        ...SurveyNavigator_surveyTemplate
    }
    ${SurveyNavigatorFragments.surveyTemplate}
`;

export const surveyResponseFragment = gql`
    fragment SurveyQueryContainer_surveyResponse on SurveyResponse {
        id
        ...SurveyNavigator_surveyResponse
    }
    ${SurveyNavigatorFragments.surveyResponse}
`;

export const SurveyQueryContainerQuery = gql`
    query SurveyQueryContainerQuery($surveyTemplateId: ID!, $surveyResponseId: ID!) {
        getSurveyTemplate(id: $surveyTemplateId) {
            ...SurveyQueryContainer_surveyTemplate
        }
        getSurveyResponse(id: $surveyResponseId) {
            ...SurveyQueryContainer_surveyResponse
        }
    }
    ${surveyTemplateFragment}
    ${surveyResponseFragment}
`;
// The mutation config
import gql from 'graphql-tag';
import * as SurveyQueryContainerGql from '../../Survey/graphql/SurveyQueryContainer';

export const createSurveyResponseMutation = gql`
    mutation createSurveyResponse($input: CreateSurveyResponseInput!) {
        createSurveyResponse(input: $input) {
            id
            surveyTemplateId
            ...SurveyQueryContainer_surveyResponse
        }
    }
    ${SurveyQueryContainerGql.surveyResponseFragment}
`;

export const getCreateSurveyResponseMutationConfig = ({ variables }) => ({
    mutation: createSurveyResponseMutation,
    variables,
    optimisticResponse: {
        createSurveyResponse: {
            ...variables.input,
            __typename: 'SurveyResponse',
            questionResponses: { __typename: 'ModelQuestionResponseConnection', items: [] },
        },
    },
    update: (store, { data: { createSurveyResponse } }) => {
                // createSurveyResponse looks exactly as I expect during BOTH updates
        let getSurveyTemplate;
        try {
            getSurveyTemplate = store.readFragment({
                id: `SurveyTemplate:${createSurveyResponse.surveyTemplateId}`,
                fragment: SurveyQueryContainerGql.surveyTemplateFragment,
                fragmentName: 'SurveyQueryContainer_surveyTemplate',
            });
        } catch (e) {
            console.warn("Could not read survey template from store", e);
        }

        store.writeQuery({
            query: SurveyQueryContainerGql.SurveyQueryContainerQuery,
            variables: {
                surveyTemplateId: createSurveyResponse.surveyTemplateId,
                surveyResponseId: createSurveyResponse.id,
            },
            data: {
                getSurveyTemplate,
                getSurveyResponse: createSurveyResponse,
            },
        });

        // const result = store.readQuery({
        //  query: SurveyQueryContainerGql.SurveyQueryContainerQuery,
        //  variables: {
        //      surveyTemplateId: createSurveyResponse.surveyTemplateId,
        //      surveyResponseId: createSurveyResponse.id,
        //  },
        // });
        //
        // console.log('ITS ALWAYS CORRECT HERE!', result.getSurveyResponse);
    },
});

What is the expected behavior? That data.getSurveyResponse is not null after the response from the server

Which versions and which environment (browser, react-native, nodejs) / OS are affected by this issue? Did this work in previous versions? React Native + Expo (iOS and Android)

// package.json
{
    ...
    "aws-amplify": "^3.0.10",
    "aws-appsync": "^3.0.3",
    "aws-appsync-react": "^3.0.3",
    ...
    "expo": "^36.0.0",
    ...
    "react": "~16.8.3",
    "react-apollo": "~2.4.1",
    "react-native": "~0.61.0",
    ...
}

Unknown if it works in other versions