How to execute query on every click using useLazyQuery() - reactjs

Using useLazyQuery() hooks from #apollo/react-hooks I was able to execute a query on click of a button. But I cannot use it execute same query on consecutive clicks.
export default ({ queryVariables }) => {
const [sendQuery, { data, loading }] = useLazyQuery(GET_DIRECTION, {
variables: queryVariables
});
return <div onClick={sendQuery}>Click here</div>;
}
In the above the sendQuery executes only once.

useLazyQuery uses the default network policy that of cache-first So I supposed your onClick function actually executes but because the returned value is what was in the cache, React notices no change in data as such the state is not updated since the returned data is what it already has that way no re-render and thing seem not to have changed. I suggest you should pass in a different network policy something like
const [sendQuery, { data, loading }] = useLazyQuery(GET_DIRECTION, {
variables: queryVariables,
fetchPolicy: "network-only"
});
This will mean you want the most recent information from your api hence basically no caching.
You might also want to experiment on other option and see which one best suits you
like cache-and-network: you can find out a little more here understanding-apollo-fetch-policies

As per the docs, useLazyQuery accepts the parameter fetchPolicy.
const [lazyQuery, { data, loading }] = useLazyQuery(QUERY, {
fetchPolicy: 'no-cache'
});
With fetchPolicy set to no-cache, the query's result is not stored in the cache.

Related

Disable short polling for RTK Query on window not focused and revalidate on resume focus

I am using Redux Toolkit with RTK Query to manage the API calls and states in my React application.
For a particular API, I am trying to mimic a real-time update effect with short polling, and calling the API defined in the API reducer by,
const { data, error, isLoading } = useGetAPIQuery(
{},
{ pollingInterval: 5000 }
);
However, I want the short polling to stop when the user has left the window, such as switching the focused tab to another tab, and revalidating the data once the window is focused again.
Is this possible with RTK query?
Similiar question also asked as issue posted on Github
Sure, you can use a package like use-window-focus to detect the focus.
const windowFocused = useWindowFocus();
const { data, error, isLoading } = useGetAPIQuery(
{},
{ pollingInterval: windowFocused ? 5000 : false }
);
I found out about the conditional fetching in the docs
we can use a hook to check if the window is in focus, for example the use-window-focus library
then pass this boolean into the skip option, and the page will be automatically and immediately revalidaite on window refocus
const { data, error, isLoading } = useGetAPIQuery(
{},
{ pollingInterval: 5000, skip: isWindowFocused }
);

Order of cache items after update mutation using Apollo Client

I have a question concerning Apollo Client's behaviour when dispatching an update mutation.
I have a little application that fetches data and allows you to modify it. After the modification, an update mutation is sent to graphQL. The changes can be seen instantly on the UI since the update of a single item triggers an automatic cache update by Apollo.
However, I noticed that when I refresh the page after an update, the order of the items I previously fetched is changed with the recently updated item going at the end of the list.
I was just wondering if this is the normal behaviour to expect and if there was a way to force the cache to keep the same order after an update?
Edit: Here's the code for my resolver, mutation and useMutation call.
Resolver:
async UpdateUser(parent, args, ctx, info) {
const { id, input } = args;
const updatedUser = await ctx.prisma.user.update({
where: {
id,
},
data: {
...input,
},
});
return updatedUser;
}
Mutation:
export const UPDATE_USER_MUTATION = gql`
mutation UpdateUser($id: String, $input: CreateUserInput) {
UpdateUser(id: $id, input: $input) {
id
name
email
}
}
`;
useMutation:
UpdateField({
variables: {
id: data.fieldID,
input: {
[data.fieldName]: value,
},
},
});
Edit 2: Here's a gif what's going on..
Thank you!
Mutation update option can update (or insert) properly list/array (query cached result) following BE sorting ...
... but it will fail on longer datasets, paginated results - on list query refetch record can be removed from current [page] list/array. It will be more confusing behavior.
IMHO fighting is not worth the effort, it's already acceptable behavior (mutation of indexed/ordering field).
Possible solutions:
don't refetch list query, only update the query list cache (mutation update - it will update the list view);
for small datasets, use FE sortable table component (BE order doesn't matter).

Apollo useLazyQuery hook uses old variables when refetching (instead of new vars)

My database stores a list of items. I've written a query to randomly return one item from the list each time a button is clicked. I use useLazyQuery and call the returned function to execute the query and it works fine. I can call the refetch function on subsequent button presses and it returns another random item correctly.
The problem comes when i try to pass variables to the query. I want to provide different criteria to the query to tailor how the random choice is made. Whatever variables I pass to the first call are repeated on each refetch, even though they have changed. I can trace that they are different on the client but the resolver traces the previous variables.
// My query
const PICK = gql`
query Pick($options: PickOptionsInput) {
pick(options: $options) {
title
}
}
`;
// My lazy hook
const [pick, { data, refetch }] = useLazyQuery(PICK, {
fetchPolicy: "no-cache",
});
// My button
<MyButton
onPick={(options) =>
(refetch || pick)({ // Refetch unless this is the first click, then pick
variables: {
options // Options is a custom object of data used to control the pick
},
})
}
/>
Some things I've tried:
Various cache policies
Not using an object for the options and defining each possible option as a different variable
I'm really stumped. It seems like the docs say new variables are supposed to be used if they are provided on refetch. I don't think the cache policy is relevant...I'm getting fresh results on each call, it's just the input variables that are stale.
I guess only pick is called ...
... because for <MyBytton/> there is no props change, no onPick redefined - always the first handler definition used (from first render) and options state from the same time ...
Try to pass all (options, pick and refetch) as direct props to force rerendering and handler redefinition:
<MyButton
options={options}
pick={pick}
refetch={refetch}
onPick={(options) =>
(refetch || pick)({ // Refetch unless this is the first click, then pick
variables: {
options // Options is a custom object of data used to control the pick
},
})
}
/>
... or [better] define handler before passing it into <MyButton/>:
const pickHandler = useCallback(
(options) => {
if(refetch) {
console.log('refetch', options);
refetch( { variables: { options }});
} else {
console.log('pick', options);
pick( { variables: { options }});
}
},
[options, pick, refetch]
);
<MyButton onPick={pickHandler} />

GraphQL Automatic refetch on empty responses

I want to randomize movies from theMovieDB API. First I send a request to access the ID of the latest entry:
const { loading: loadingLatest, error: errorLatest, data: latestData, refetch: refetchLatest } = useQuery(
LATEST_MOVIE_QUERY
);
Then I want to fetch data from a randomly selected ID between 1 and the number of the latest id. Using a variable dependant on the first query seems to break the app, so for now I'm just using the same movie every time upon mounting the component:
const [
movieState,
setMovieState
] = useState(120);
const { loading, error, data, refetch } = useQuery(ONE_MOVIE_BY_ID_QUERY, {
variables : { movieId: movieState },
skip : !latestData
});
I want to press a button to fetch a new random movie, but the problem is that many of the IDs in the API lead to deleted entries and then I get an error back. I want to keep refetching until I get a good response back but I have no idea to implement it. Right now my randomize function just looks like this:
const randomizeClick = () => {
let mostRecentID = latestData.latestMovie.id;
setMovieState(Math.floor(Math.random() * mostRecentID));
};
I'd be grateful if someone can help me how to implement this.
I think what you needs is the "useLazyQuery" functionality of Apollo. You can find more information about it here: https://www.apollographql.com/docs/react/data/queries/#executing-queries-manually
With useLazyQuery you can change your variables easily and this is meant to be fired after a certain event (click or something similar). The useQuery functionality will be loaded when the component is mounted.

React-Apollo - unclear usage of readQuery/writeQuery in mutation's update function

I'm not clear at all about how readQuery and writeQuery work in the context of the update function passed to a mutation function in react-apollo.
It seems that readQuery returns a reference to the cache, mutating the returned object directly triggers a rerender across my components with the relevant changes, why then do I need to call writeQuery?
Example directly from the docs (https://www.apollographql.com/docs/react/features/optimistic-ui.html) of an update function passed into mutate :
update: (proxy, { data: { submitComment } }) => {
// Read the data from our cache for this query.
const data = proxy.readQuery({ query: CommentAppQuery });
// Add our comment from the mutation to the end.
data.comments.push(submitComment);
// Write our data back to the cache.
proxy.writeQuery({ query: CommentAppQuery, data });
}
now if I comment out the last line :
update: (proxy, { data: { submitComment } }) => {
// Read the data from our cache for this query.
const data = proxy.readQuery({ query: CommentAppQuery });
// Add our comment from the mutation to the end.
data.comments.push(submitComment);
// Write our data back to the cache.
// proxy.writeQuery({ query: CommentAppQuery, data });
}
My component and UI would change as soon as the data returned from proxy.readQuery is mutated. What added benefit is there to calling proxy.writeQuery?
Initially I thought proxy.writeQuery would be necessary to notify React of the changes and trigger a re-render to my components, but evidently this is not the case.

Resources