Currently I have a useLazyQuery
hook which is fired on a button press (part of a search form).
The hook behaves normally, and is only fired when the button is pressed. However, once I've fired it once, it's then fired every time the component re-renders (usually due to state changes).
So if I search once, then edit the search fields, the results appear immediately, and I don't have to click on the search button again.
Not the UI I want, and it causes an error if you delete the search text entirely (as it's trying to search with null
as the variable), is there any way to prevent the useLazyQuery
from being refetched on re-render?
This can be worked around using useQuery
dependent on a 'searching' state which gets toggled on when I click on the button. However I'd rather see if I can avoid adding complexity to the component.
const AddCardSidebar = props => {
const [searching, toggleSearching] = useState(false);
const [searchParams, setSearchParams] = useState({
name: ''
});
const [searchResults, setSearchResults] = useState([]);
const [selectedCard, setSelectedCard] = useState();
const [searchCardsQuery, searchCardsQueryResponse] = useLazyQuery(SEARCH_CARDS, {
variables: { searchParams },
onCompleted() {
setSearchResults(searchCardsQueryResponse.data.searchCards.cards);
}
});
...
return (
<div>
<h1>AddCardSidebar</h1>
<div>
{searchResults.length !== 0 &&
searchResults.map(result => {
return (
<img
key={result.scryfall_id}
src={result.image_uris.small}
alt={result.name}
onClick={() => setSelectedCard(result.scryfall_id)}
/>
);
})}
</div>
<form>
...
<button type='button' onClick={() => searchCardsQuery()}>
Search
</button>
</form>
...
</div>
);
};
The useLazyQuery function returns a promise that fulfills with a query result when the query succeeds or fails.
Unlike with useQuery , when you call useLazyQuery , it does not immediately execute its associated query. Instead, it returns a query function in its result tuple that you call whenever you're ready to execute the query.
Whether your query gets its data from the cache or from the API depends on which fetch policy that query is using. The fetch policy tells Apollo whether to prioritize getting the most recent data from the server or getting a faster response from the cache.
refetch() The fetchApi() function in our useQuery Hook is the function we'll want to run again if we needed to refetch query information and update state. In the useQuery. ts file, we'll restructure how our Hook is set up to have the fetchApi() function be returned from the Hook.
You don't have to use async
with the apollo client (you can, it works). But if you want to use useLazyQuery
you just have to pass variables on the onClick
and not directly on the useLazyQuery call.
With the above example, the solution would be:
function DelayedQuery() {
const [dog, setDog] = useState(null);
const [getDogPhoto] = useLazyQuery(GET_DOG_PHOTO, {
onCompleted: data => setDog(data.dog)
})
return (
<div>
{dog && <img src={dog.displayImage} />}
<button
onClick={() => getDogPhoto({ variables: { breed: 'bulldog' }})}
>
Click me!
</button>
</div>
);
}
The react-apollo
documentation doesn't mention whether useLazyQuery
should continue to fire the query when variables change, however they do suggest using the useApolloClient
hook when you want to manually fire a query. They have an example which matches this use case (clicking a button fires the query).
function DelayedQuery() {
const [dog, setDog] = useState(null);
const client = useApolloClient();
return (
<div>
{dog && <img src={dog.displayImage} />}
<button
onClick={async () => {
const { data } = await client.query({
query: GET_DOG_PHOTO,
variables: { breed: 'bulldog' },
});
setDog(data.dog);
}}
>
Click me!
</button>
</div>
);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With