title: How to pass state array with multiple objects as a parameter to graphql mutation?
body: <p>I am creating an app that allows users to create lists (all data is stored on mongoDB and accessed via Apollo GraphQL); each list has a property, listItems, an array that stores all the items in the list. </p>
  
  <p>Currently I am now able to move the listItems around and have them setState in an array accessed via state.items as shown <a href="https://i.imgur.com/Jl1eARc.mp4" rel="nofollow noreferrer">in this video</a>.  The list movement is controlled by components found within <a href="https://github.com/clauderic/react-sortable-hoc" rel="nofollow noreferrer">react-sortable-hoc library</a></p>
  
  <p>The issue I am having is taking this state.items array of listItems and using a mutation to have the state.items array replace the backend respective list.listItems array in the database.  </p>
  
  <p>I believe I may have not properly setup the typeDefs for input ListItems (I was getting <strong>Error: The type of Mutation.editListItems(listItems:) must be Input Type but got: [ListItem]!.</strong> so I changed it to input instead of type which may be an issue?)</p>
  
  <p>Lastly I attempt to call the editListItems mutation in the RankList functional component below (contained in RankList.js) 'onChange'.  I am not sure where the issue and when moving items, it seems the mutation is not called.  Please advise (let me know if I should include more info)! </p>
  
  <p>Overall, I wonder if the following could be an issue:</p>
  
  <ul>
  <li><em>Is the typeDef for editListItems properly implemented?</em></li>
  <li><em>Is onChange the right thing to use here?</em>  </li>
  <li><em>Am I calling the mutation properly (in RankList component)?</em></li>
  <li><em>Am I properly passing state.items in the editListItems mutation (in RankList component)?</em></li>
  </ul>
  
  <p><strong>typeDefs.js</strong></p>
  
  <pre><code>const { gql } = require("apollo-server");
  
  //schema
  
  module.exports = gql`
    type List {
      id: ID!
      title: String!
      createdAt: String!
      username: String!
      listItems: [ListItem]!
      comments: [Comment]!
      likes: [Like]!
      likeCount: Int!
      commentCount: Int!
    }
    type ListItem {
      id: ID!
      createdAt: String!
      username: String!
      body: String!
    }
    input ListItems {
      id: ID!
      createdAt: String!
      username: String!
      body: String!
    }
    type Comment {
      id: ID!
      createdAt: String!
      username: String!
      body: String!
    }
    type Like {
      id: ID!
      createdAt: String!
      username: String!
    }
    type User {
      id: ID!
      email: String!
      token: String!
      username: String!
      createdAt: String!
    }
    input RegisterInput {
      username: String!
      password: String!
      confirmPassword: String!
      email: String!
    }
    type Query {
      getLists: [List]
      getList(listId: ID!): List
    }
    type Mutation {
      register(registerInput: RegisterInput): User!
      login(username: String!, password: String!): User!
      createList(title: String!): List!
      editListItems(listId: ID!, listItems: ListItems!): List!
      deleteList(listId: ID!): String!
      createListItem(listId: ID!, body: String!): List!
      deleteListItem(listId: ID!, listItemId: ID!): List!
      createComment(listId: ID!, body: String!): List!
      deleteComment(listId: ID!, commentId: ID!): List!
      likeList(listId: ID!): List!
    },
    type Subscription{
      newList: List!
    }
  `;
  </code></pre>
  
  <p><strong>list.js (resolvers)</strong></p>
  
  <pre><code>async editListItems(_, { listId, listItems }, context) {
        console.log("editListItems Mutation activated!");
        const user = checkAuth(context);
  
        const list = await List.findById(listId);
  
        if (list) {
          if (user.username === list.username) {
            list.listItems = listItems;
            await list.save();
            return list;
          } else {
            throw new AuthenticationError("Action not allowed");
          }
        } else {
          throw new UserInputError("List not found");
        }
      },
  </code></pre>
  
  <p><strong>RankList.js (List component shown in the video above)</strong></p>
  
  <pre><code>import React, { useContext, useEffect, useRef, useState } from "react";
  import gql from "graphql-tag";
  import { useQuery, useMutation } from "@apollo/react-hooks";
  import { Form } from "semantic-ui-react";
  import moment from "moment";
  
  import { AuthContext } from "../context/auth";
  import { SortableContainer, SortableElement } from "react-sortable-hoc";
  import arrayMove from "array-move";
  import "../RankList.css";
  import { CSSTransitionGroup } from "react-transition-group";
  
  const SortableItem = SortableElement(({ value }) =&gt; (
    &lt;li className="listLI"&gt;{value}&lt;/li&gt;
  ));
  
  const SortableList = SortableContainer(({ items }) =&gt; {
    return (
      &lt;ol className="theList"&gt;
        &lt;CSSTransitionGroup
          transitionName="ranklist"
          transitionEnterTimeout={500}
          transitionLeaveTimeout={300}
        &gt;
          {items.map((item, index) =&gt; (
            &lt;SortableItem
              key={`item-${item.id}`}
              index={index}
              value={item.body}
            /&gt;
          ))}
        &lt;/CSSTransitionGroup&gt;
      &lt;/ol&gt;
    );
  });
  
  function RankList(props) {
    const listId = props.match.params.listId;
    const { user } = useContext(AuthContext);
    const listItemInputRef = useRef(null);
  
    const [state, setState] = useState({ items: [] });
    const [listItem, setListItem] = useState("");
  
    const { loading, error, data } = useQuery(FETCH_LIST_QUERY, {
      variables: {
        listId,
      },
      onError(err) {
        console.log(err.graphQLErrors[0].extensions.exception.errors);
        // setErrors(err.graphQLErrors[0].extensions.exception.errors);
      }
    });
  
    useEffect(() =&gt; {
      if (data &amp;&amp; data.getList &amp;&amp; data.getList.listItems) {
        setState(() =&gt; ({ items: data.getList.listItems }));
      }
    }, [data]);
  
    // const [state, setState] = useState({ items: data.getList.listItems });
  
    const [submitListItem] = useMutation(SUBMIT_LIST_ITEM_MUTATION, {
      update() {
        setListItem("");
        listItemInputRef.current.blur();
      },
      variables: {
        listId,
        body: listItem,
      },
    });
  
    const [editListItems] = useMutation(EDIT_LIST_ITEMS_MUTATION, {
      variables: {
        listId,
        listItems: state.items,
      },
    });
  
    if (loading) return &lt;p&gt;Loading...&lt;/p&gt;;
    if (error) return &lt;p&gt;Error..&lt;/p&gt;;
  
    function deleteListCallback() {
      props.history.push("/");
    }
  
    function onSortEnd({ oldIndex, newIndex }) {
      setState(({ items }) =&gt; ({
        items: arrayMove(items, oldIndex, newIndex),
      }));
      //THIS MAY BE WHERE THE ISSUE LIES
      editListItems();
    }
  
    let listMarkup;
    if (!data.getList) {
      listMarkup = &lt;p&gt;Loading list...&lt;/p&gt;;
    } else {
      const {
        id,
        title,
        createdAt,
        username,
        listItems,
        comments,
        likes,
        likeCount,
        commentCount,
      } = data.getList;
  
      listMarkup = user ? (
        &lt;div className="todoListMain"&gt;
          &lt;div className="rankListMain"&gt;
            &lt;div className="rankItemInput"&gt;
              &lt;h3&gt;{title}&lt;/h3&gt;
              &lt;Form&gt;
                &lt;div className="ui action input fluid"&gt;
                  &lt;input
                    type="text"
                    placeholder="Choose rank item.."
                    name="listItem"
                    value={listItem}
                    onChange={(event) =&gt; setListItem(event.target.value)}
                    ref={listItemInputRef}
                  /&gt;
                  &lt;button
                    type="submit"
                    className="ui button teal"
                    disabled={listItem.trim() === ""}
                    onClick={submitListItem}
                  &gt;
                    Submit
                  &lt;/button&gt;
                &lt;/div&gt;
              &lt;/Form&gt;
            &lt;/div&gt;
            &lt;SortableList
              items={state.items}
              onSortEnd={onSortEnd}
              helperClass="helperLI"
            /&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      ) : (
        &lt;div className="todoListMain"&gt;
          &lt;div className="rankListMain"&gt;
            &lt;div className="rankItemInput"&gt;
              &lt;h3&gt;{props.title}&lt;/h3&gt;
            &lt;/div&gt;
            &lt;SortableList
              items={listItems}
              onSortEnd={onSortEnd}
              helperClass="helperLI"
            /&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      );
    }
  
    return listMarkup;
  }
  
  const EDIT_LIST_ITEMS_MUTATION = gql`
    mutation($listId: ID!, $listItems: ListItems!) {
      editListItems(listId: $listId, listItems: $listItems) {
        id
        listItems {
          id
          body
          createdAt
          username
        }
      }
    }
  `;
  
  const SUBMIT_LIST_ITEM_MUTATION = gql`
    mutation($listId: ID!, $body: String!) {
      createListItem(listId: $listId, body: $body) {
        id
        listItems {
          id
          body
          createdAt
          username
        }
        comments {
          id
          body
          createdAt
          username
        }
        commentCount
      }
    }
  `;
  
  const FETCH_LIST_QUERY = gql`
    query($listId: ID!) {
      getList(listId: $listId) {
        id
        title
        createdAt
        username
        listItems {
          id
          createdAt
          username
          body
        }
        likeCount
        likes {
          username
        }
        commentCount
        comments {
          id
          username
          createdAt
          body
        }
      }
    }
  `;
  
  export default RankList;
  </code></pre>