简体   繁体   中英

Rails/GraphQL: Uncaught (in promise): Error: Cannot read property of undefined

I'm working on a Rails/React/GraphQl project and getting an Uncaught (in promise): Error: Cannot read property of undefined in the RegisterUser mutation. I searched for similar questions on Google but couldn't find anything similar. I'm pretty new to React/GraphQL/Apollo .

When there is a ActiveReacord error it's been successfully logged to the console with this setup.

On a success I get the success message plus the above mentioned Uncaught Error printed to the console. Also, I see in rails console the new user was created, but the form hangs and doesn't get close (I'm assuming due to the error?).

Here's where the error is coming from: (at line .then(({ data: { registerUser: { success, errors: [{ message }] } } }) => )

RegisterUser/index.js:

import React, { useState } from 'react';
import { useHistory } from "react-router-dom";
import { Mutation } from 'react-apollo';
import { Register } from './operations.graphql';


const RegisterUser = () => {
  const isLoggedIn = JSON.parse(sessionStorage.getItem('isLoggedIn'));
  const history = useHistory();
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [email, setEmail] = useState("");

  const onCompleted = (resp) => {
    console.log(resp.registerUser.success);
  }

  const onError = (message) => {
    console.log(message);
  }

    if(!isLoggedIn) {
      return (
        <Mutation mutation={Register} onCompleted={onCompleted}>
          {(registerUser, { loading, error }) => (
            <div className="card bg-light col-md-4 offset-md-4 shadow p-3 mb-5 rounded">
              <article className="card-body mx-auto" style={{maxWidth: '400px'}}>
                <p className="text-center">Create an account</p>
                <form onSubmit={ e => {
                    e.preventDefault();
                    registerUser({
                      variables: {
                        username: username,
                        password: password,
                        email:    email,
                      }
                    }).then(({ data: { registerUser: { success, errors: [{ message }] } } }) => {
                      if (success) {
                        history.push("/login");
                      } else {
                        onError(message);
                      }
                    });
                  }}
                >
                  <div className="form-group input-group">
                    <input
                      type="text"
                      value={username}
                      className="form-control"
                      onChange={e => setUsername(e.target.value)}
                      placeholder="username"
                      required
                    />
                  </div>
                  <div className={"form-group input-group"}>
                    <input
                      type="email"
                      value={email}
                      className="form-control"
                      onChange={e => setEmail(e.target.value)}
                      placeholder="email"
                      required
                    />
                  </div>
                  <div className="form-group input-group">
                    <input
                      type="password"
                      value={password}
                      className="form-control"
                      onChange={e => setPassword(e.target.value)}
                      placeholder="password"
                      required
                    />
                  </div>
                  <input type="submit" className="btn btn-primary btn-block" value="Sign up!" />
                  <p className="text-center">Have an account? <a href="/login">Log In</a> </p>
                </form>
              </article>
              {loading && <p>Loading...</p>}
              {error && <p>Error! Please try again</p>}
            </div>
          )}
        </Mutation>
      );
    }
}
export default RegisterUser;

register_user.rb:

module Mutations
  class RegisterUser < Mutations::BaseMutation
    graphql_name "RegisterUser"

    argument :attributes, Types::UserRegistrationAttributes, required: true

    field :user, Types::UserType, null: true

    def resolve(attributes:)
      user = User.new(username: attributes[:username],
                      password: attributes[:password],
                      email: attributes[:email])
      if user.save
        # current_user needs to be set so authenticationToken can be returned
        context[:current_user] = user
        {
          user: user,
          errors: [],
          success: "Sign up successfull. Please confirm your email",
        }
      else
        user_errors = user.errors.messages.map do |key, message|
          path = ["attributes", key.to_s.camelize(:lower)]
          {
            path: path,
            message: "#{key.to_s.camelize} #{message}",
          }
        end
        {
          user: user,
          errors: user_errors,
        }
      end
    end
  end
end

base_mutation.rb:

module Mutations
  # This class is used as a parent for all mutations, and it is the place to have common utilities
  class BaseMutation < GraphQL::Schema::Mutation

    field :success, String, null: true
    field :errors, [Types::UserErrorType], null: true

    protected

    def authorize_user
      return true if context[:current_user].present?

      raise GraphQL::ExecutionError, "User not signed in"
    end
  end
end

user_error_type.rb:

module Types
  class UserErrorType < Types::BaseObject
    description "A user-readable error"

    field :message, String, null: true, description: "A description of the error"
    field :path, [String], null: true, description: "Which input value this error came from"
  end
end

Not sure what causing this error, any suggestions are greatly appreciated. Thank you for your time.

UPDATE(S): 1/29/2020 issue was fixed based on @xadm's reply bellow.

1) Register

operations.graphql

mutation Register($username: String!, $password: String!, $email: String!) {
  __typename
  registerUser(attributes: {username: $username, password: $password, email: $email}) {
    user {
      id
      username
      authenticationToken
    }
    success
    errors {
      message
      path
    }
  }
}
```

Test in external tool prooves that it's not [quite] react/apollo fault.

It's from not standard error handling - optional elements (message) expressed in mutation, expected in response. Similiar problem using UNION: useQuery returns undefined, But returns data on gql playground

Error that you're pointing at:

Here's where the error is coming from: (at line .then(({ data: { registerUser: { success, errors: [{ message }] } } }) =>)

is an example of "too optimistic destructuring", not fail safe (not stops on nulls), change it to

.then(({ data: { registerUser: { success, errors } } }) =>)

and process errors in body - this is an array so no one message parameter.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM