Is it possible to prevent `useLazyQuery` queries from being re-fetched on component state change / re-render?

16,082

Solution 1

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

Solution 2

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>
  );
}
Share:
16,082
user11754604
Author by

user11754604

Updated on June 05, 2022

Comments

  • user11754604
    user11754604 almost 2 years

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