Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do I use onComplete with useQuery ? #106

Open
bluedusk opened this issue Mar 15, 2019 · 17 comments
Open

How do I use onComplete with useQuery ? #106

bluedusk opened this issue Mar 15, 2019 · 17 comments

Comments

@bluedusk
Copy link

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 !

@janhesters
Copy link

janhesters commented Mar 17, 2019

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?

@leoc
Copy link

leoc commented Mar 19, 2019

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

@pak11273
Copy link

pak11273 commented Apr 8, 2019

@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?

@leoc
Copy link

leoc commented Apr 8, 2019

@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.

@pak11273
Copy link

pak11273 commented Apr 8, 2019

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>
   }
}

@fbartho
Copy link

fbartho commented Apr 8, 2019

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!)

@pak11273
Copy link

pak11273 commented Apr 8, 2019

@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.

@vmptk
Copy link

vmptk commented Apr 8, 2019

@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">

...

`

@pak11273
Copy link

pak11273 commented Apr 8, 2019

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

@leoc
Copy link

leoc commented Apr 9, 2019

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.

@pak11273
Copy link

pak11273 commented Apr 9, 2019

@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.

@cziem
Copy link

cziem commented Jul 27, 2019

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

@leoc
Copy link

leoc commented Jul 31, 2019

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
Copy link

leoc commented Jul 31, 2019

@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.

@ncux199rus
Copy link

it works for me like this:

const [token, setToken] = useState()

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

@mdeividas
Copy link

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;

@hickscorp
Copy link

hickscorp commented Feb 29, 2024

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants