react-apollo-hooks
react-apollo-hooks copied to clipboard
How do I use onComplete with useQuery ?
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 !
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?
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 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 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.
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>
}
}
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 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, 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 No. It only fires once. The code I have works, it just bugs me that I have to silence an eslint warning.
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 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.
Can you use useQuery()
from within the useEffect()
hook?
Can you use
useQuery()
from within theuseEffect()
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 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.
it works for me like this:
const [token, setToken] = useState()
useQuery(queryGetTocken, {
onCompleted: (data) => {
setToken(data.auth.access)
},
})
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;
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?