Apollo-client: After using useLazyQuery hook, "called" parameter remains "true" when the component re-renders

Created on 14 Aug 2019  路  5Comments  路  Source: apollographql/apollo-client

I'm trying to use useLazyQuery hook, in a function component, and execute the query on button click. So, i'm using the "called" parameter alongside "loading" to check if my "Navigate to Google" button was pressed and the query was executed successfully to continue executing my desired action (in this case navigate to www.google.com).

On the first execution everything works as expected but when i'm executing another action in the same screen that triggers a re-render, the "called" parameter from the previous action is still "true" so my previous action is executed again even though i didn't pressed the "Navigate to Google" button again.

Here is my TestFunctionComponent. To reproduce the issue first press the "Navigate to Google" button and then press "Force re-render" button.

In my application i use the query to retrieve the link to navigate on button click.

const useForceUpdate = () => useState()[1];

const TestFunctionComponent = () => {

    const forceUpdate = useForceUpdate()
    const [executeQuery, { called, loading, data }] = useLazyQuery(MY_QUERY);

    if (called && !loading) {
        window.open(data .navigationLink, '_blank')
    }

    const handleOpenAnalysisFormPdf = () => {
        return executeQuery({
            variables: {
                medicalDocumentId: 6089249
            },
        });
    }

    return (
        <Fragment>
            <Button variant="outlined" onClick={handleOpenAnalysisFormPdf}>
                {"Navigate to Google"}
            </Button>
            <Button variant="outlined" onClick={forceUpdate}>
                {"Force re-render"}
            </Button>
        </Fragment>
    )
}

export default TestFunctionComponent;

Thank you!

Most helpful comment

So, i'm using the "called" parameter alongside "loading" to check if my "Navigate to Google" button was pressed and the query was executed successfully to continue executing my desired action (in this case navigate to www.google.com).

If you provide an onCompleted callback to useLazyQuery if will be executed once your query completes successfully. See docs for options.

I don't think this fixes the underlying issue. Unless the called can be reset to false via an exposed method or setter, it's set once a single query is made, regardless of it's response.

My use case is to authenticate an input against a graphql endpoint, on "Submit" I invoke the query via useLazyQuery, check if the response is successful and then navigate away or show an error. It would be good to be able to set called to false until it's next called via useLazyQuery.

All 5 comments

I have a similar issue:

const [getSimilarProjects, { loading, data }] = useLazyQuery(SIMILAR_PROJECTS_QUERY, {
    variables: {
      id: project.id,
    },
  })

This one works:

 const handleSimilarProjects = useCallback(() => {
    getSimilarProjects()
  }, [getSimilarProjects])

This one dosen't:

 const handletoggleFollow = useCallback(() => {
    getSimilarProjects()

    toggleFollow({
      variables: {
        id: 123,
      },
    })
  }, [getSimilarProjects, toggleFollow, project])

but if I use each mutation separate they work.

@pontusab This is not really my case, my problem is that after the first execution of the query was done successfully and this line window.open(data .navigationLink, '_blank') is executed, on any other action executed on this screen that triggers a re-render, that windows.open command is triggered again and again because called parameter from my if statement is never false.

So, i'm using the "called" parameter alongside "loading" to check if my "Navigate to Google" button was pressed and the query was executed successfully to continue executing my desired action (in this case navigate to www.google.com).

If you provide an onCompleted callback to useLazyQuery if will be executed once your query completes successfully. See docs for options.

So, i'm using the "called" parameter alongside "loading" to check if my "Navigate to Google" button was pressed and the query was executed successfully to continue executing my desired action (in this case navigate to www.google.com).

If you provide an onCompleted callback to useLazyQuery if will be executed once your query completes successfully. See docs for options.

I don't think this fixes the underlying issue. Unless the called can be reset to false via an exposed method or setter, it's set once a single query is made, regardless of it's response.

My use case is to authenticate an input against a graphql endpoint, on "Submit" I invoke the query via useLazyQuery, check if the response is successful and then navigate away or show an error. It would be good to be able to set called to false until it's next called via useLazyQuery.

I too have encountered this very same issue. The called attribute is not reset on re-render. A workaround I've found that might be useful considering your scenario is to use useQuery with the skip parameter set to true, in conjunction with the useEffect hook to control what to do on a re-render. This way you can still call the refetch() method, and the behavior will be similar to the one expected from a lazy query.

Was this page helpful?
0 / 5 - 0 ratings