Apollo Client useMutation data always returns undefined
Asked Answered
B

3

6

I have the following code that runs a mutation to update a post to "published" the mutation works great! It updates the data as expected. However, the data property is always undefined in the useMutation hook. This is odd because I can see the response has data in it in the network tab. I'm quite stumped on this issue. Help would be appreciated. Here is the react code:

import { gql, useMutation } from "@apollo/client";
import React from "react";
import { Spinner } from "react-bootstrap";
import "./Post.css";

const PUBLISH_POST = gql`
  mutation PublishPost($id: ID!) {
    postPublish(id: $id) {
      userErrors {
        message
      }
      post {
        title
      }
    }
  }
`;

const UNPUBLISH_POST = gql`
  mutation UnPublishPost($id: ID!) {
    postUnpublish(id: $id) {
      userErrors {
        message
      }
      post {
        title
      }
    }
  }
`;
export default function Post({
  title,
  content,
  date,
  user,
  published,
  id,
  isMyProfile
}) {

  const [publishPost, { data, loading, error }] = useMutation(PUBLISH_POST);
 console.log("data", data);
  const [UnpublishPost, { data: unpublishData, loading: unpublishLoading }] = useMutation(UNPUBLISH_POST);
 
  const formatedDate = new Date(Number(date)).toDateString();

  if (loading || unpublishLoading) {
    return <Spinner animation="border" />;
  }
  if (data?.userErrors?.length) {
    return (
      <div>
        {data.userErrors.map(e => {
          return <p>{e?.message}</p>;
        })}
      </div>
    );
  }
  if (unpublishData?.userErrors?.length) {
    return (
      <div>
        {unpublishData.userErrors.map(e => {
          return <p>{e?.message}</p>;
        })}
      </div>
    );
  }
  return (
    <div
      className="Post"
      style={published === false ? { backgroundColor: "hotpink" } : {}}
    >
      <div>ID: {id}</div>
      {isMyProfile && published === false && (
        <p
          className="Post__publish"
          onClick={() => {
            publishPost({
              variables: {
                id
              }
            });
          }}
        >
          publish
        </p>
      )}
      {isMyProfile && published === true && (
        <p
          className="Post__publish"
          onClick={() => {
            UnpublishPost({
              variables: {
                id
              }
            });
          }}
        >
          unpublish
        </p>
      )}
      <div className="Post__header-container">
        <h2>{title}</h2>
        <h4>
          Created At {formatedDate} by {user}
        </h4>
      </div>
      <p>{content}</p>
    </div>
  );
}

And here is the graphql code that runs on the server (I doubt this section is the problem but you never know)

  postPublish: async (
    _: any,
    { id }: { id: string },
    { prisma, userInfo }: Context
  ): Promise<PostPayloadType> => {
    const payLoad = new PayLoad();
    if (!userInfo) {
      payLoad.addError("you must be logged in");
      return payLoad;
    }
    const error = await canUserMutatePost(userInfo.userId, Number(id), prisma);

    if (error.userErrors.length) {
      return error;
    }
    payLoad.post = await prisma.post.update({
      where: {
        id: Number(id)
      },
      data: {
        published: true
      }
    });
    return payLoad;
  }

also, I've noticed if I await publishPost I do eventually get data however I think there's a problem in the setup as I should just be able to use useMutation as above.

Here is a picture of the network response: enter image description here

Balkin answered 21/4, 2022 at 15:49 Comment(4)
Can you make a minimal reproducible example? Does anything in your application cause <Post> to be re-mounted when (un)published?Sarong
The code is all there. What would cause that?Balkin
it could be an error when parsing the response, check if it returns an error { data: unpublishData, loading: unpublishLoading, error: unpublishError }Spiros
Did you set ignoreResults to true somehow? See the docOwsley
L
0

The agree with the answer told by @jeffreyquan . He explained the way how the value of data changes. If you want to trigger an event/function when you get the data then you should use useEffect like below:

    useEffect (() => {

        if (unpublishData){
            // Your code to triggered when we get the data from graphql
        }

    }, [unpublishData])

YouTube tutorial for learning how to do it: https://www.youtube.com/watch?v=YyUWW04HwKY

Github repo with one example: https://github.com/machadop1407/GraphQL-ApolloClient-Template/tree/main/client

Laoighis answered 2/8, 2022 at 1:43 Comment(0)
C
-1

From what I can see, there's no reason data should always be undefined, especially since a post can go from unpublished to published using the mutation and you see a response in the network tab.

If you have the following console.log:

console.log({
  data,
  error,
  loading,
});

What you will see in the console at the various stages of the mutation is:

  1. Before mutation is fired
{data: undefined, error: undefined, loading: false}
  1. Right after mutation is fired - loading changes from false to true
{data: undefined, error: undefined, loading: true}
  1. After the mutation is successful - receive response data and loading changes from true to false
{
  data: {
    postPublish: {
      post: {
        title: "My Post Title", // the title of the post you are publishing
      },
      __typename: "Insert type name"
    },
  },
  error: undefined,
  loading: false,
}

This will also be reflected on the UI.

Just a couple of questions:

  1. After you publish a post successfully, do you see the option to un-publish it after?
  2. What is the response you see in the network tab after you fire off the mutation?
Coleoptile answered 24/4, 2022 at 5:38 Comment(3)
the answer to question 1 is no because I am not refetching queries but if I refresh the page the query did work and I can see the update. I will attach a screenshot of the response aboveBalkin
While I can see you are trying to help and I appreciate it your answer still does not provide a solution to the problem. It merely states how the function should work. I already understand this. When the question is updated I will mark it as helpful considering there is a bounty on the line.Balkin
No problems at all. I am definitely trying to help. A minimal reproducible example would help as it means we can run it locally to see what is happening and whether it is an environment issue. The other suggestion I would have if you require the data from a successful mutation is to use the onCompleted option for the useMutation hook.Coleoptile
B
-1

While I was not able to figure out why data was undefined I was able to call the update function and receive the data I needed. Still unsure why data is undefined when used without this.

  const [publishPost, { loading }] = useMutation(PUBLISH_POST, {
    update(_, result) {
      console.log(result);
      navigate("/");
    });

https://www.apollographql.com/docs/react/data/mutations/

Maybe it has something to do with caching: "When a mutation's response is insufficient to update all modified fields in your cache (such as certain list fields), you can define an update function to apply manual changes to your cached data after a mutation."

Balkin answered 30/4, 2022 at 7:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.