0
votes

I have an UpdateUserPage which can call mutations in different ways depending on how many input variables are being passed for eg:

only firstname is updated

    mutation{
            updateUser(email: "[email protected]",
            input: {firstName:"check"}){id}
            }

both, firstname & lastName are updated:

    mutation{
            updateUser(email: "[email protected]",
            input: {firstName:"check", lastName: "new"}){id}
            }

Query in my code right now:

export const UPDATE_USER = gql`
  mutation UpdateUser($email: String!, $firstName: String, $lastName: String) {
    updateUser(
      email: $email
      input: { firstName: $firstName, lastName: $lastName }
    ) {
      id
      firstName
    }
  }
`;

What I want:

I want to change my form in such a way that if the user enters only the firstName and leaves the lastName field blank, then only firstName should be updated, meaning that the first type of query should be used and the lastName should remain what it was before the mutation was called.

What's Happening Now:

For instance, previously the firstName was HELLO and lastName was WORLD. Now, I am updating the firstName to CHECK and leaving the lastName option blank. So when it returns values, it should return CHECK as the firstName and un-modified WORLD as the lastName. However, right now, it shows lastName as undefined. If I inspect the Resources, I see this:

{"operationName":"UpdateUser","variables":{"email":"[email protected]","firstName":"Final"},"query":"mutation UpdateUser($email: String!, $firstName: String, $lastName: String) {\n updateUser(email: $email, input: {firstName: $firstName, lastName: $lastName}) {\n id\n firstName\n __typename\n }\n}\n"} Looks like the lastName is still being passed in the mutation.

which means that the lastName is still being used in the mutation. So, I need a way to pass custom variables into my query:

$input : Object!doesn't work.

Here's the code for my whole page:

export default function UpdateUserPage() {
  const [isSubmitted, setIsSubmitted] = useState(false);
  const [isUpdated, setIsUpdated] = useState(false);

  const [updateUser] = useMutation(UPDATE_USER);
  let submitForm = (
    email: string,
    firstName: string,
    lastName: string,
  ) => {
    setIsSubmitted(true);

    if (email && (firstName || lastName || phoneNumber)) {
      const vars:any = {
        email
      };
    if( firstName !== ''){
      vars.firstName = firstName}
      if( lastName !== ''){
        vars.lastName = lastName}

      updateUser({
        variables: vars})
        .then(({ data }: any) => {
          setIsUpdated(true);
          console.log(updateUser);
          console.log('FN: ', data.updateUser.firstName);
          console.log('LN: ', data.updateUser.lastName);
        })
        .catch((error: { message: string }) => {
          setIsUpdated(false);
        });
    }
  };

  return (
    <div>
      <Formik
        initialValues={{
          firstName: '',
          lastName: '',
          email: '',
          phoneNumber: '',
        }}
        onSubmit={(values, actions) => {
          setTimeout(() => {
            alert(JSON.stringify(values, null, 2));
            actions.setSubmitting(false);
          }, 1000);
        }}
        validationSchema={schema}>
        {props => {
          const {
            values: { firstName, lastName, email, phoneNumber },
            errors,
            touched,
            handleChange,
            isValid,
            setFieldTouched,
          } = props;
          const change = (name: string, e: FormEvent) => {
            e.persist();
            handleChange(e);
            setFieldTouched(name, true, false);
          };
          return (
            <div className="main-content">
              <form
                style={{ width: '100%' }}
                onSubmit={e => {
                  e.preventDefault();
                  submitForm(email, firstName, lastName);
                }}>
                <div>
                  <TextField
                    variant="outlined"
                    margin="normal"
                    id="email"
                    name="email"
                    helperText={touched.email ? errors.email : ''}
                    error={touched.email && Boolean(errors.email)}
                    label="Email"
                    value={email}
                    onChange={change.bind(null, 'email')}
                  />
                  <br></br>
                  <TextField
                    variant="outlined"
                    margin="normal"
                    id="firstName"
                    name="firstName"
                    helperText={touched.firstName ? errors.firstName : ''}
                    error={touched.firstName && Boolean(errors.firstName)}
                    label="First Name"
                    value={firstName}
                    onChange={change.bind(null, 'firstName')}
                  />
                  <br></br>
                  <TextField
                    variant="outlined"
                    margin="normal"
                    id="lastName"
                    name="lastName"
                    helperText={touched.lastName ? errors.lastName : ''}
                    error={touched.lastName && Boolean(errors.lastName)}
                    label="Last Name"
                    value={lastName}
                    onChange={change.bind(null, 'lastName')}
                  />
                  <CustomButton
                    text={'Update User Info'}
                  />
                </div>
              </form>
            </div>
          );
        }}
      </Formik>
    </div>
  );
}

Edit:

From the schema:

input: UpdateUserInput!
input UpdateUserInput {
  email: String
  firstName: String
  lastName: String
  phoneNumber: String
}

Now, I have done this:

interface UpdateUserInput {
  email: String
  firstName: String
  lastName: String
  phoneNumber: String
}

export const UPDATE_USER = gql`
  mutation UpdateUser($email: String!, $input : UpdateUserInput) {
    updateUser(
      email: $email
      input: $input
    ) {
      id
      firstName
    }
  }
`;

However, when I submit the form, I still get this error:

GraphQL error: The variable `input` type is not compatible with the type of the argument `input`.
Expected type: `UpdateUserInput`.

Probably because I'm still passing vars from my main UpdateUserPage. How would I change that?

2

2 Answers

2
votes

The type of your $input variable would be whatever is the type of the input argument on updateUser. If you didn't write the GraphQL schema yourself, you would need to check your API's documentation or GraphiQL/GraphQL Playground interface to determine the type to use.

Using a single variable ($input) instead of multiple ones for each field ($firstName and $lastName) is the preferred way of doing things. That said, there is nothing wrong with your query as is. You current query:

mutation ($email: String!, $firstName: String, $lastName: String) {
  updateUser(
    email: $email
    input: { firstName: $firstName, lastName: $lastName }
  ){
    id
  }
}

with the variables {"email": "[email protected]", "firstName": "Final"} would be equivalent to

mutation ($email: String!, $input: WhateverYourInputTypeIs) {
  updateUser(
    email: $email
    input: $input
  ){
    id
  }
}

with {"email": "[email protected]", "input": { "firstName": "Final"} }.

In both cases, the arguments passed to the resolver on the server side will be the same. If you are ending up with the wrong value for lastName after the mutation runs, then this is an issue with how the resolver is handling the arguments it receives.

0
votes

You probably need to modify your interface so that the fields are not required.

interface UpdateUserInput {
  email?: String
  firstName?: String
  lastName?: String
  phoneNumber?: String
}

By adding a question mark to the fields you are making them optional so that you can pass one or more at a time.