0
votes

I'm working on a React Native project with Apollo Client and GraphQL as the backend. The problem I'm having is when I register a new user using a mutation, I get back a positive response that the user has been created, but I also get thrown an error saying Error caught: TypeError: Cannot read property 'variables' of undefined

I'm not sure what the problem is, or why the error is being returned at all. Here's the code:

'use strict'

import React, { Component } from 'react';
import { Text, View, StyleSheet, AsyncStorage, Modal, TouchableHighlight } from 'react-native'
import { Actions, ActionConst } from 'react-native-router-flux'
import { Button } from 'react-native-elements'

// Apollo and GraphQL Packages
import ApolloClient, { graphql, withApollo } from 'react-apollo'
import gql from 'graphql-tag'
import { filter } from 'graphql-anywhere'
import { connect } from 'react-redux'
import Auth0Lock from 'react-native-lock'

// Styling Packages
import LinearGradient from 'react-native-linear-gradient'
import EStyleSheet from 'react-native-extended-stylesheet'

// View Packages
import ViewContainer from './ViewContainer'

// Auth0 Credentials
const clientId = "XXXX"
const domain = "XXXX"

// Props Declaration
type Props = {
  client: ApolloClient,
  createUser: ({
    variables: {
      idToken: string,
      name: ?string,
      email: ?string
    }
  }) => { id: string}
}

class Login extends Component {
  _lock: Auth0Lock
  props: Props

  constructor(props) {
    super(props)
    this._lock = new Auth0Lock({
      clientId: clientId,
      domain: domain,
      useBrowser: true
    })
  }

  _showLogin() {
    this._lock.show({
      closable: true,
      connections: ['Username-Password-Authentication']
    }, async (err, profile, token) => {
      if (!err) {
        AsyncStorage.setItem('token', token.idToken)
          .then(() => {
            this.props.client.resetStore()
          })
        this.props.createUser({
            variables: {
              idToken: token.idToken,
              name: profile.name
              email: profile.email
            }
          })
          .then((data) => {
            console.log("Data received: " + data)
            Actions.registration({ type: ActionConst.REPLACE })
          })
          .catch((err) => {
            console.log("Error caught: " + err)
            AsyncStorage.removeItem('token')
              .then(() => this.props.client.resetStore())
            // Actions.home({ type: ActionConst.REPLACE })
          })
      } else {
        console.log(err)
      }
    })
  }

  render() {
    return(
      <LinearGradient colors={['#34E89E', '#0F3443']} style={styles.linearGradient}>
        <ViewContainer style={styles.viewContainer}>
          <Button
            small
            raised
            buttonStyle= {styles.button}
            color="#fff"
            title="Login"
            onPress={() => this._showLogin()} />
        </ViewContainer>
      </LinearGradient>
    )
  }
}

const styles = EStyleSheet.create({
  viewContainer: {
    flex: 1,
    paddingTop: 70,
    paddingLeft: 20,
    paddingRight: 20
  },
  linearGradient: {
    height: '$height',
  },
  logo: {
    fontFamily: 'LiberatorHeavy',
    fontSize: 52,
    alignSelf: 'center',
    marginBottom: 400,
    color: 'white',
    backgroundColor: 'transparent'
  },
  button: {
    backgroundColor: '#222222'
  }
});

const createUserMutation = gql`
mutation createUser($idToken: String!, $name: String, $email: String) {
  createUser(
    authProvider: {
      auth0: {
        idToken: $idToken
      }
    },
    name: $name,
    email: $email
  ){
    id
  }
}`

export default withApollo(
  graphql(createUserMutation,
    { name: 'createUser' }
  )(Login))
1
Are you sure the error is coming from this component in particular? One thing I would try is printing out "this.props" inside your if statement, as "this" can be dangerous in the wrong scope, as its value is determined at runtimemstorkson
Yeah, I'm sure this is the component that's throwing the error - specifically this line: .catch((err) => {console.log("Error caught: " + err)} Also, looking through the dev console, I can see the correct variables are passing so it's succeeding. What I can't figure out is why it's throwing an error after I get the success response from the server.Kaidao
Well the error means something is searching for x.variables and x being null. The only time variables seems to be called is in the mutation. Can you check and see if the query is firing twice by mistake, the second time having no passed variables?mstorkson
@mstorkson You were right - it was firing two times for some reason, although I'm still not sure why. Changing it to include the createUser within AsyncStorage.setItem('token'...) worked. It only fired once and returned a positive response. I'm not 100% sure why this is happening, but at least it works for now. Thanks for your helpKaidao
No problem, glad to have helpedmstorkson

1 Answers

0
votes

So the error was that the

createUser({variables...})

function was firing twice which resulted in the error being thrown once the variables were cleared. I changed my code to:

_showLogin() {
    this._lock.show({
      closable: true,
      connections: ['Username-Password-Authentication']
    }, async (err, profile, token) => {
      if (!err) {
        AsyncStorage.setItem('token', token.idToken)
          .then(() => {
            this.props.client.resetStore()

            // Create user function was put in to AsyncStorage function to stop function from running twice. (Not sure why that was happening)
            this.props.createUser({
                variables: {
                  idToken: token.idToken,
                  name: profile.name,
                  email: profile.email
                }
              })
              .then((data) => {
                console.log("Data received: " + data)
                Actions.registration({ type: ActionConst.REPLACE })
              })
              .catch((err) => {
                console.log("Error caught: " + err)
                AsyncStorage.removeItem('token')
                  .then(() => this.props.client.resetStore())
                // Actions.home({ type: ActionConst.REPLACE })
              })
          })
      } else {
        console.log(err)
      }

This solved the problem and I was getting the proper response, although I'm still not sure why it was running twice after the Async function.

If anyone has any ideas, please let me know!