react-apollo-hooks icon indicating copy to clipboard operation
react-apollo-hooks copied to clipboard

How do I use onComplete with useQuery ?

Open bluedusk opened this issue 5 years ago • 17 comments

Hi guys,

I want to do something after each polling but couldn't find how to use onCompleted:

onCompleted: (data: TData | {}) => void A callback executed once your query successfully completes.

Any idea how to use this with useQuery ? Thanks !

bluedusk avatar Mar 15 '19 05:03 bluedusk

I'm facing the same issue. I'm using this with AWS Amplify.

Before my code looked like this:

  async function fetchContacts() {
    try {
      const contactData = await API.graphql(
        graphqlOperation(queries.listContacts)
      );
      setContacts(
        replaceNillInArrayWithEmptyString(contactData.data.listContacts.items)
      );
    } catch (err) {
      console.log('error: ', err);
    }
  }

  useEffect(() => {
    fetchContacts();
  }, []);

Now, I would need . something like onCompleted. I'm thinking about useEffect maybe? Like this:

const { data, error, loading } = useQuery(queries.listContacts);

useEffect(() => {
  if(!error && !loading) {
    setContacts(
      replaceNillInArrayWithEmptyString(data.listContacts.items)
    );
  }
}, [data, error, loading])

Is this okay?

janhesters avatar Mar 17 '19 21:03 janhesters

I just stumbled over this too. @janhesters, your useEffect looks good.

The react-apollo Query component does it like this:

https://github.com/apollographql/react-apollo/blob/5cb63b3625ce5e4a3d3e4ba132eaec2a38ef5d90/src/Query.tsx#L228-L239

Following the same logic:

  const { loading, data, error } = useQuery(query);

  useEffect(() => {
    const onCompleted = (data) => { /* magic */ };
    const onError = (error) => { /* magic */ };
    if (onCompleted || onError) {
      if (onCompleted && !loading && !error) {
        onCompleted(data);
      } else if (onError && !loading && error) {
        onError(error);
      }
    }
  }, [loading, data, error]);

If react-apollo-hooks included this I think there should either be a call to the callback to the specified in the props or a useEffect after the useMemo that does this:

https://github.com/trojanowski/react-apollo-hooks/blob/master/src/useQuery.ts#L134-L154

leoc avatar Mar 19 '19 10:03 leoc

@leoc If I add useState logic in the onCompleted function I get caught in a loop. I can get fix this by adding state to the dependency array but then I get a warning about missing dependency: state. If I put the state in the dependency I get the loop again. Is there a work around for this or can we not use useState in the onCompleted function?

pak11273 avatar Apr 08 '19 13:04 pak11273

@pak11273 Could you post example code? Generally you can only use hooks (e.g. useState) within functional react components. So you should not put useState into callbacks. What you could do is calling set<State> from within callbacks though.

leoc avatar Apr 08 '19 14:04 leoc

This is the functional react component I'm using. I'm also using react-apollo-hooks but I don't think that is affecting anything.

 const CourseUpdate = () => {
   const [state, changeState] = useState({
     data: []
   })

   const {data, error, loading} = useQuery(getBooks, {
     variables: {
       courseId: course._id
     }
   })

   useEffect(
     () => {
       const onCompleted = data => {
         changeState({
           ...state,
           data: data.getBooks.books
         })
       }
       const onError = error => {
         return (
           <div>{error}</div>
         )
       }
       if (onCompleted || onError) {
         if (onCompleted && !loading && !error) {
           onCompleted(data)
         } else if (onError && !loading && error) {
           onError(error)
         }
       }
     },
     [loading, data, error]
   )

   if (loading) {
     return <div>...loading</div>
   }
}

pak11273 avatar Apr 08 '19 15:04 pak11273

I haven't worked much with useEffect yet, but I think you want to push the useQuery inside the useEffect callback.

Assuming that you want useEffect at all. -- Are you sure you need useEffect?

You could instead simply load the data and render it. (Remove the useEffect entirely!)

fbartho avatar Apr 08 '19 16:04 fbartho

@fbartho I need useEffect to mimic componentDidMount. The useQuery function is basically another react hook, so it can't be used inside of a callback, it needs to be inside of a react functional component.

pak11273 avatar Apr 08 '19 17:04 pak11273

@pak11273, Does the useQuery triggered twice in the snippet? I have similar implementation and observe at least double execution of the query.

`const ClientRiskSummaryPanel = () => { const {dispatch} = useContext(Context); const {data, error, loading} = useQuery(QUERY);

useEffect(() => {
    const onCompleted = data => {
        dispatch({type: ADD_MESSAGE, payload: data.getMessage});
    };
    const onError = error => {
        return <Error message={error} />;
    };
    if (onCompleted || onError) {
        if (onCompleted && !loading && !error) {
            onCompleted(data);
        } else if (onError && !loading && error) {
            onError(error);
        }
    }
}, [loading, data, error]);

if (loading) {
    return <div>Loading...</div>;
}

return (
    <List component="nav">

... </List> `

vmptk avatar Apr 08 '19 18:04 vmptk

@vmptk No. It only fires once. The code I have works, it just bugs me that I have to silence an eslint warning.

pak11273 avatar Apr 08 '19 19:04 pak11273

Normally I put [all, my, query, variables] into the useQuery to avoid multiple runs when other state is changing.

@pak11273 I don't understand why you would need another state (useState). Using the data from useQuery should suffice, no?

I needed the onComplete to trigger another query which needed data from the first query.

leoc avatar Apr 09 '19 10:04 leoc

@leoc Maybe I can just use the data from useQuery, I'll try that. I was putting data into useState because that data is a list of books which will be manipulated by users. I generally put data from api calls into state. I don't know if that's a good practice or not.

pak11273 avatar Apr 09 '19 13:04 pak11273

Can you use useQuery() from within the useEffect() hook?

cziem avatar Jul 27 '19 10:07 cziem

Can you use useQuery() from within the useEffect() hook?

Hi @phavor, no you can't, since react-hooks cannot be used in the callbacks of react-hooks. I suggest you read up on using react-hooks with functional components. This helps a lot. The concept might be confusing otherwise :)

leoc avatar Jul 31 '19 15:07 leoc

@leoc Maybe I can just use the data from useQuery, I'll try that. I was putting data into useState because that data is a list of books which will be manipulated by users. I generally put data from api calls into state. I don't know if that's a good practice or not.

The useQuery composed hook should actually use a useState inside for the data. So you can just use the state from the useQuery return value.

leoc avatar Jul 31 '19 15:07 leoc

it works for me like this:

const [token, setToken] = useState()

useQuery(queryGetTocken, {
  onCompleted: (data) => {
    setToken(data.auth.access)
  },
})

ncux199rus avatar Jul 28 '21 12:07 ncux199rus

it works for me like this:

const [token, setToken] = useState()

useQuery(queryGetTocken, {
  onCompleted: (data) => {
    setToken(data.auth.access)
  },
})

Probably this is already outdated, but what I did find is that you can use onSettled instead of onCompleted

this is from the docs:

/**
* This callback will fire any time the query is either successfully fetched or errors and be passed either the data or error.
*/
onSettled?: (data: TData | undefined, error: TError | null) => void;

mdeividas avatar Aug 09 '23 08:08 mdeividas

I'm also failing what's the idiomatic way to use state management with useQuery in conjunction to most React idiomatic effect management hooks.

I've been told recently that using useEffect on the data of a useQuery result is a bad idea - but havn't been told "why" to make myself my own opinion... I tried to implement the same thing using exclusivelly useQuery's onCompleted callback - but it doesn't get called when re-triggering the query from another query (Eg using the refetchQueries options).

Being fluent in React, I find it frustrating that the documentation is a bit all over the place, and there's no single FAQ on good practices for the React Apollo hooks. Anyone please?

hickscorp avatar Feb 29 '24 12:02 hickscorp