react-apollo icon indicating copy to clipboard operation
react-apollo copied to clipboard

TypeError: Cannot read property 'data' of undefined at MutationData.onMutationCompleted (react-hooks.cjs.js:654)

Open azuxx opened this issue 4 years ago • 4 comments

Intended outcome:

Hello, I have a React Native app but I cannot let my Mutation work even though it seems I have configured Apollo Client properly. Here is the relevant code of SignIn.tsx

//...other code...
const MUTATION_SIGN_IN = gql`
  mutation userSignIn($input: SignInInput!) {
    userSignIn(input: $input) {
      authHeaders {
        accessToken
        client
        expiry
        tokenType
        uid
      }
      user {
        id
      }
    }
  }
`;
//...other code...
const [formModel, setFormModel] = useState({
    email: '',
    password: ''
  });
const [validationErrors, setValidationErrors] = useState({
    login: false,
    forgotPassword: false
  });
const [userSignIn, {data, loading: signInLoading}] = useMutation(
    MUTATION_SIGN_IN
  );
const onLoginPress = () => {
    userSignIn({
      variables: {
        input: {
          email: {
            ...formModel
          }
        }
      }
    })
      .then(({data: res, errors}) => {
       // Execution never goes here
        console.log(res);
        console.log(errors);
        setValidationErrors((prev) => ({...prev, login: !!errors}));
      })
      .catch((reason) => {
       // But always goes here with the error specified below
        console.log(reason);
        setValidationErrors((prev) => ({...prev, login: !!reason}));
      });
  };
  //...other code till the relative part of jsx which is the button...
   <Button
            mode="contained"
            loading={signInLoading}
            disabled={formModelEmpty()}
            onPress={onLoginPress}>
            {translations.SIGNIN_LOGIN_BUTTON}
    </Button>

Just to be complete SignInInput! has the following generated schema

input AuthProviderEmailSignInInput {
  email: String!
  password: String!
}
input SignUpInput {
  email: AuthProviderEmailSignUpInput
}

Actual outcome:

Error occurs. Console error has this:

TypeError: Cannot read property 'data' of undefined at MutationData.onMutationCompleted (react-hooks.cjs.js:654) at react-hooks.cjs.js:579 at tryCallOne (core.js:37) at core.js:123 at JSTimers.js:277 at _callTimer (JSTimers.js:135) at _callImmediatesPass (JSTimers.js:183) at Object.callImmediates (JSTimers.js:446) at MessageQueue.__callImmediates (MessageQueue.js:396) at MessageQueue.js:144

Attaching debugging I tried to follow before the error occurs:

  1. first here 1

  2. then here 2

response is undefined so response.data breaks. Response seems always undefined (both cases of right credentials and wrong ones), and also execution seems always go in the "complete" event function. I was not able to understand the root cause of this.

How to reproduce the issue:

My React Native project has the package.json specied below. Other useful code other than SignIn.tsx is this:

  1. Return template of my App.tsx
return (
    <LocalizationProvider>
      <SafeAreaProvider>
        <ApolloProvider client={client}>
          <AuthContext.Provider value={authContext}>
            <NavigationContainer>
              {!authState.userToken ? (
                <Stack.Navigator>
                  {authState.isLoading ? (
                    // We haven't finished checking for the token yet
                    <Stack.Screen
                      name="Splash"
                      component={Splash}
                      options={{headerShown: false}}
                    />
                  ) : (
                    // No token found, user isn't signed in
                      <Stack.Screen
                        name="SignIn"
                        component={SignInScreen}
                        options={{
                          headerTitle: (props) => <LogoTitle {...props} />,
                          headerStyle: {
                            height: 100
                          },
                          // When logging out, a pop animation feels intuitive
                          animationTypeForReplace: authState.isSignout
                            ? 'pop'
                            : 'push'
                        }}
                      />
                  )}
                </Stack.Navigator>
              ) : (
                // User is signed in
                <AppNavigator />
              )}
            </NavigationContainer>
          </AuthContext.Provider>
        </ApolloProvider>
      </SafeAreaProvider>
    </LocalizationProvider>
  );

client variable is Imported from apollo-client.ts which is this (I followed your Migration guide for Custom configuration):

const errorLink = onError(({graphQLErrors, networkError}) => {
  if (graphQLErrors) {
    graphQLErrors.map(({message, locations, path}) =>
      console.log(
        `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
      )
    );
  }
  if (networkError) {
    console.log(`[Network error]: ${networkError}`);
  }
});

const request = async (operation) => {
  //uses async storage to get the saved headers if available
  const newHeaders = getHeaders();
  operation.setContext({
    headers: {
      ...newHeaders
    }
  });
};

const requestLink = new ApolloLink(
  (operation, forward) =>
    new Observable((observer) => {
      // @ts-ignore
      let handle;
      Promise.resolve(operation)
        .then((oper) => request(oper))
        .then(() => {
          handle = forward(operation).subscribe({
            next: observer.next.bind(observer),
            error: observer.error.bind(observer),
            complete: observer.complete.bind(observer)
          });
        })
        .catch(observer.error.bind(observer));

      return () => {
        // @ts-ignore
        if (handle) {
          // @ts-ignore
          handle.unsubscribe();
        }
      };
    })
);

//this is needed for union types
const fragmentMatcher = new IntrospectionFragmentMatcher({
  introspectionQueryResultData: introspectionQueryResultData as any
});

const cache = new InMemoryCache({
  addTypename: true,
  fragmentMatcher
});

const Client = new ApolloClient({
  link: ApolloLink.from([
    errorLink,
    requestLink,
    withClientState({
      defaults: {
        isConnected: true
      },
      resolvers: {
        Mutation: {
          updateNetworkStatus: (_, {isConnected}, {cache}) => {
            cache.writeData({data: {isConnected}});
            return null;
          }
        }
      },
      cache
    }),
    split(
      ({query: {definitions}}) =>
        definitions.some((node) => {
          const {kind, operation} = node as OperationDefinitionNode;
          return kind === 'OperationDefinition' && operation === 'subscription';
        }),
      new HttpLink({
        uri: GRAPHQL_ENDPOINT
      })
    )
  ]),
  cache,
  connectToDevTools: true
});
export default Client;

Version

Here's the relevant part of my package.json:

"dependencies": {
    "@apollo/react-hooks": "^3.1.4",
    "@react-native-community/async-storage": "^1.8.1",
    "@react-native-community/masked-view": "^0.1.7",
    "@react-navigation/bottom-tabs": "^5.2.6",
    "@react-navigation/native": "^5.1.5",
    "@react-navigation/stack": "^5.2.10",
    "apollo-cache-inmemory": "^1.6.5",
    "apollo-client": "^2.6.8",
    "apollo-link": "^1.2.13",
    "apollo-link-context": "^1.0.19",
    "apollo-link-error": "^1.1.12",
    "apollo-link-http": "^1.5.16",
    "apollo-link-lazy": "^0.0.2",
    "apollo-link-state": "^0.4.2",
    "apollo-utilities": "^1.3.3",
    "babel-plugin-module-resolver": "^4.0.0",
    "graphql-tag": "^2.10.3",
    "react": "16.11.0",
    "react-native": "0.62.2",
    "react-native-gesture-handler": "^1.6.1",
    "react-native-localization": "^2.1.6",
    "react-native-localize": "^1.3.4",
    "react-native-paper": "^3.8.0",
    "react-native-reanimated": "^1.7.1",
    "react-native-safe-area-context": "^0.7.3",
    "react-native-screens": "^2.4.0",
    "react-native-vector-icons": "^6.6.0"
  },
  "devDependencies": {
    "@babel/core": "^7.6.2",
    "@babel/runtime": "^7.6.2",
    "@graphql-codegen/cli": "^1.13.1",
    "@graphql-codegen/fragment-matcher": "1.13.1",
    "@graphql-codegen/introspection": "1.13.1",
    "@graphql-codegen/typescript": "1.13.1",
    "@graphql-codegen/typescript-operations": "1.13.1",
    "@graphql-codegen/typescript-react-apollo": "1.13.1",
    "@react-native-community/eslint-config": "^1.0.0",
    "@types/graphql": "14.2.3",
    "@types/jest": "^24.0.24",
    "@types/react": "^16.9.32",
    "@types/react-native": "^0.62.0",
    "@types/react-native-dotenv": "^0.2.0",
    "@types/react-test-renderer": "16.9.2",
    "@typescript-eslint/eslint-plugin": "^2.25.0",
    "@typescript-eslint/parser": "^2.25.0",
    "babel-jest": "^24.9.0",
    "eslint": "^6.5.1",
    "eslint-import-resolver-babel-module": "^5.1.2",
    "eslint-plugin-import": "^2.20.2",
    "jest": "^24.9.0",
    "metro-react-native-babel-preset": "^0.58.0",
    "prettier": "^2.0.2",
    "react-devtools": "^4.6.0",
    "react-native-dotenv": "^0.2.0",
    "react-test-renderer": "16.11.0",
    "typescript": "^3.8.3"
  },
  "resolutions": {
    "graphql": "14.5.8"
  },

Let me know if more information is needed and I will try to add detail the best that I can. Thank you very much, Andrea

azuxx avatar Apr 10 '20 11:04 azuxx

👍 Same error.

tuananhhedspibk avatar Apr 18 '20 08:04 tuananhhedspibk

Same error.

I gave up stable version. Yesterday I tried 3.0.0-beta.43 with new configuration things and it worked. Anyhow, it would be liked to make v2.x working as well.

azuxx avatar Apr 18 '20 19:04 azuxx

For me, I have changed the return value of API, everything is OK now.

tuananhhedspibk avatar Apr 19 '20 04:04 tuananhhedspibk

Pasting working apollo-client code with apollo 3 Beta. (nothing changed in the other files instead).

import {
  ApolloClient,
  ApolloLink,
  createHttpLink,
  InMemoryCache
} from '@apollo/client';
// @ts-ignore
import {GRAPHQL_ENDPOINT} from 'react-native-dotenv';
import {onError} from '@apollo/link-error';
import {getHeaders} from '@utils/authentication';
import {Observable} from '@apollo/client/utilities/observables/Observable';

console.log(GRAPHQL_ENDPOINT);

const httpLink = createHttpLink({
  uri: GRAPHQL_ENDPOINT
});

const errorLink = onError(({graphQLErrors, networkError}) => {
  if (graphQLErrors) {
    graphQLErrors.map(({message, locations, path}) =>
      console.log(
        `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
      )
    );
  }
  if (networkError) {
    console.log(`[Network error]: ${networkError}`);
  }
});

const request = async (operation) => {
  //uses async storage to get the saved headers if available
  const newHeaders = await getHeaders();
  operation.setContext({
    headers: {
      ...newHeaders
    }
  });
};

const requestLink = new ApolloLink(
  (operation, forward) =>
    new Observable((observer) => {
      // @ts-ignore
      let handle;
      Promise.resolve(operation)
        .then((oper) => request(oper))
        .then(() => {
          handle = forward(operation).subscribe({
            next: observer.next.bind(observer),
            error: observer.error.bind(observer),
            complete: observer.complete.bind(observer)
          });
        })
        .catch(observer.error.bind(observer));

      return () => {
        // @ts-ignore
        if (handle) {
          // @ts-ignore
          handle.unsubscribe();
        }
      };
    })
);

export default new ApolloClient({
  link: ApolloLink.from([errorLink, requestLink, httpLink]),
  cache: new InMemoryCache({
    addTypename: true
  }),
  connectToDevTools: true
});

package.json

"dependencies": {
    "@apollo/client": "^3.0.0-beta.43",
    "@apollo/link-error": "^2.0.0-beta.3",
    "@react-native-community/async-storage": "^1.8.1",
    "@react-native-community/datetimepicker": "^2.3.2",
    "@react-native-community/masked-view": "^0.1.7",
    "@react-navigation/bottom-tabs": "^5.2.6",
    "@react-navigation/native": "^5.1.5",
    "@react-navigation/stack": "^5.2.10",
    "babel-plugin-module-resolver": "^4.0.0",
    "base-64": "^0.1.0",
    "jetifier": "^1.6.5",
    "react": "16.11.0",
    "react-native": "0.62.2",
    "react-native-ble-plx": "^2.0.0",
    "react-native-gesture-handler": "^1.6.1",
    "react-native-localization": "^2.1.6",
    "react-native-localize": "^1.3.4",
    "react-native-paper": "^3.8.0",
    "react-native-reanimated": "^1.7.1",
    "react-native-safe-area-context": "^0.7.3",
    "react-native-screens": "^2.4.0",
    "react-native-vector-icons": "^6.6.0"
  },
  "devDependencies": {
    "@babel/core": "^7.6.2",
    "@babel/runtime": "^7.6.2",
    "@graphql-codegen/cli": "^1.13.1",
    "@graphql-codegen/fragment-matcher": "1.13.1",
    "@graphql-codegen/introspection": "1.13.1",
    "@graphql-codegen/typescript": "1.13.1",
    "@graphql-codegen/typescript-operations": "1.13.1",
    "@graphql-codegen/typescript-react-apollo": "1.13.1",
    "@react-native-community/eslint-config": "^1.0.0",
    "@types/base-64": "^0.1.3",
    "@types/graphql": "14.2.3",
    "@types/jest": "^24.0.24",
    "@types/react": "^16.9.32",
    "@types/react-native": "^0.62.0",
    "@types/react-native-dotenv": "^0.2.0",
    "@types/react-test-renderer": "16.9.2",
    "@typescript-eslint/eslint-plugin": "^2.25.0",
    "@typescript-eslint/parser": "^2.25.0",
    "babel-jest": "^24.9.0",
    "eslint": "^6.5.1",
    "eslint-import-resolver-babel-module": "^5.1.2",
    "eslint-plugin-import": "^2.20.2",
    "graphql-tag": "^2.10.3",
    "jest": "^24.9.0",
    "metro-react-native-babel-preset": "^0.58.0",
    "prettier": "^2.0.2",
    "react-devtools": "^4.6.0",
    "react-native-dotenv": "^0.2.0",
    "react-test-renderer": "16.11.0",
    "typescript": "^3.8.3"
  },
  "resolutions": {
    "graphql": "^14.5.8"
  },

Hope this would be helpful for others.

azuxx avatar Apr 21 '20 14:04 azuxx