Search code examples
javascriptreactjsgraphqlnext.jsapollo

How to handle returned data as undefined that needs to be added to a useQuery hook in apollo and reactjs


I have the following code but it renders the cookieData undefined on the first render and query, so the query doesn't get the cookie and it fails authetication. Any way to make the query wait for the call to the cookie api to come back before running.

const { data: cookieData, error: cookieError } = useSWR(
    "/api/cookie",
    fetcher
  );

  console.log(cookieData);


  var test = `Bearer ${cookieData}`;

  const { loading, error, data } = useQuery(FORMS, {
    context: {
      headers: {
        authorization: test,
      },
    },
  });

UPDATE: I ended up using lazy query for the above, but I will try skip as well, but I have been trying to implement skip on this mutation now and it says the id is undefined, it consoles on the page but is undfined first a few times.

const addFormClicked = async (data) => {
    //console.log(data);
    const res = await createForm({
      variables: {
        name: data.name,
        user: user.id,
      },
      skip: !user.id,
    });
    console.log(res);
    Router.push(`/formBuild/${res.data.createForm._id}`);
  };

Here's the whole code for context

import { useMutation, gql } from "@apollo/client";
import Layout from "../components/Layout";
import { useForm } from "react-hook-form";
import { useRouter } from "next/router";
import { FORMS } from "../components/Layout";
import useSWR from "swr";
import { useState } from "react";

const ADD_FORM = gql`
  mutation AddForm($name: String!, $id: ID!) {
    createForm(data: { name: $name, user: { connect: $id } }) {
      name
      _id
    }
  }
`;

const fetcher = (url) => fetch(url).then((r) => r.json());

export default function AddForm() {
  const { data: user } = useSWR("/api/user"); // add

  const { data: cookieData, error: cookieError } = useSWR(
    "/api/cookie",
    fetcher
  );

  var test = `Bearer ${cookieData}`;

  const Router = useRouter();

  const [
    createForm,
    {
      data: createFormData,
      error: createFormError,
      loading: createFormLoading,
    },
  ] = useMutation(ADD_FORM, {
    refetchQueries: [{ query: FORMS }],
    context: {
      headers: {
        authorization: test,
      },
    },
  });

  const addFormClicked = async (data) => {
    //console.log(data);
    const res = await createForm({
      variables: {
        name: data.name,
        user: user.id,
      },
      skip: !user.id,
    });
    console.log(res);
    Router.push(`/formBuild/${res.data.createForm._id}`);
  };

  const { register, handleSubmit, errors, reset } = useForm();

  if (createFormLoading) return <p>Loading</p>;
  if (createFormError) return <p>Error: {createFormError.message}</p>;

  //console.log(createFormData);

  return (
    <Layout>
      <form onSubmit={handleSubmit(addFormClicked)}>
        <h1>Form Name</h1>
        <input type="text" name="name" ref={register()} />
        <button type="submit">Add Form</button>
      </form>
    </Layout>
  );
}

UPDATE: The user needed to be id, seen below

 const addFormClicked = async (data) => {
    //console.log(data);
    const res = await createForm({
      variables: {
        name: data.name,
        id: user.id, //NOT user:user.id BUT id:user.id
      },
      skip: !user.id,
    });
    console.log(res);
    Router.push(`/formBuild/${res.data.createForm._id}`);
  };

Solution

  • The user variable will be undefined while the query is in a loading state. Same with cookieData. There's no skip option available in useMutation since it does not automatically execute the mutation when the component renders.

    A simple solution would be to render the form if only if user and cookieData exist. This way, you can know for sure the user id and token will be available when the form gets submitted.

    // Add `userError` to use in combination with `user` to check if the query is loading
    const { data: user, error: userError } = useSWR('/api/user', userFetcher)
    
    const [
      createForm,
      { data: createFormData, error: createFormError, loading: createFormLoading },
    ] = useMutation(ADD_FORM, {
      refetchQueries: [{ query: FORMS }],
    })
    
    const addFormClicked = async (data) => {
      const res = await createForm({
        context: {
          headers: {
            authorization: `Bearer ${cookieData}`,
          },
        },
        variables: {
          name: data.name,
          user: user.id,
        },
      })
      Router.push(`/formBuild/${res.data.createForm._id}`)
    }
    
    if (userError || cookieError) {
      return <div>Something went wrong</div>
    }
    
    if (!user || !cookieData) {
      return <div>Loading...</div>
    }
    
    // Render form