Refetching upon a button click in RTK Query does not trigger component update
Asked Answered
Q

1

11

Hello all and thanks in advance.

Just to begin with, I am aware that weather data is not a suitable use case for RTK Query as it is always changing, I am using RTK Query in this case just for practicing purposes. I want to refetch weather data upon a button click but the component is not being updated, however I can see that the requestId is indeed changing when clicking the refresh button and that data is being retrieved, by checking the console. I am doing the refetch as in the example that can be found in the official docs:

https://redux-toolkit.js.org/rtk-query/usage/cache-behavior#re-fetching-on-demand-with-refetchinitiate

I am also wondering if it would be more suitable to use useLazyQuery and its trigger function instead...

https://redux-toolkit.js.org/rtk-query/api/created-api/hooks#uselazyquery

...but if so, what should I take into account in order to decide if I use useQuery or useLazyQuery?

    import {
  IonButton,
  IonButtons,
  IonCard,
  IonCol,
  IonContent,
  IonGrid,
  IonHeader,
  IonPage,
  IonRow,
  IonTitle,
  IonToolbar,
} from "@ionic/react";
import styles from "./Tab1.module.css";
import { RefreshOutline } from "react-ionicons";
import { Geolocation } from "@capacitor/geolocation";
import { useEffect } from "react";
import { RootState } from "../app/store";
import { useDispatch, useSelector } from "react-redux";
import { useGetCurrentPositionWeatherQuery } from "../services/weather";
import { setQueryCoord } from "../app/coordQuerySlice";
import TestSkeleton from "../components/TestSkeleton";

const Tab1: React.FC = (): JSX.Element => {
  const dispatch = useDispatch();
  const coordQueryState = useSelector((state: RootState) => state.coordQuery);
  const {
    requestId,
    refetch,
    data: weatherData,
    isLoading,
  } = useGetCurrentPositionWeatherQuery(
    {
      lat: coordQueryState.lat,
      lon: coordQueryState.lon,
      appid: "xxxx",
    },
    { skip: coordQueryState.skip }
  );

  const setCurrentPosition = async () => {
    const data = await Geolocation.getCurrentPosition();
    const {
      coords: { latitude: latFetched },
      coords: { longitude: lonFetched },
    } = data;
    dispatch(setQueryCoord({ lat: latFetched, lon: lonFetched, skip: false }));
  };

  useEffect(() => {
    setCurrentPosition();
  }, []);

  function refreshCurrentPositionHandler() {
    refetch();
  }

  console.log(requestId);
  return (
    <IonPage>
      <IonHeader>
        <IonToolbar className={styles["ion-toolbar-dashboard"]}>
          <IonTitle className="ion-margin-bottom" size="large">
            Dashboard
          </IonTitle>
          <IonButtons slot="end">
            <IonButton>
              <RefreshOutline
                onClick={refreshCurrentPositionHandler}
                color={"black"}
                height="35px"
                width="35px"
                cssClasses={styles.refreshOutline}
              />
            </IonButton>
          </IonButtons>
        </IonToolbar>
      </IonHeader>
      <IonContent fullscreen>
        <>{!weatherData && <TestSkeleton />}</>
        {weatherData && (
          <>
            <IonGrid>
              <IonRow style={{ margin: "10px" }}>
                <IonCol className="ion-text-center">
                  <h1 style={{ fontSize: "20px" }}>
                    Here's your location based weather
                  </h1>
                </IonCol>
              </IonRow>
            </IonGrid>
            <IonGrid>
              <IonCard>
                <IonRow>
                  <IonCol className="ion-text-center">test content 1</IonCol>
                </IonRow>
                <IonRow>
                  <IonCol className="ion-text-center">test content 2</IonCol>
                </IonRow>
                <IonRow>
                  <IonCol className="ion-text-center">test content 3</IonCol>
                </IonRow>
                <IonRow>
                  <IonCol></IonCol>
                  <IonCol></IonCol>
                </IonRow>
                <IonRow>
                  <IonCol></IonCol>
                </IonRow>
                <IonRow>
                  <IonCol></IonCol>
                </IonRow>
                <IonRow>
                  <IonCol></IonCol>
                </IonRow>
              </IonCard>
            </IonGrid>
          </>
        )}
      </IonContent>
    </IonPage>
  );
};

export default Tab1;
Quesenberry answered 23/2, 2022 at 3:38 Comment(3)
Actually I was just illuminated with the fact that I am using the wrong condition for rendering the content, it must be isFetching since weatherData is always true. Nevertheless, It would be great if someone can explain the part about when using useQuery and useLazyQuery, regarding refetch() and trigger() functions, thanks!Quesenberry
thanks alot brother. You've fixed my problem :). I was stuck in same issue. anyway you can check difference here. #63682150Valedictory
So glad to have helped you brother! ;)Quesenberry
C
34

I'm not allowed to leave a comment yet, so I'll try to answer you question regarding useLazyQuery vs useQuery here:

useQuery - got triggered on render. Retriggered on params change. Returns data from cache if the cache has some valid data. refetch allows to ignore cache, and forces to make a real API call. refetch has no query params, so you'll need to pass them to initial hook call.

useLazyQuery - got triggered firstly on trigger call, expecting the query params to be passed to trigger, and by default will do a real API call. To get the cached data first (if exists) - you'll need to pass the preferCacheValue param to the trigger function as the second param.

So the "rule of thumb" will be to use useQuery by default to fetch data on the component mount, use refetch when you want to make a real API call ignoring the cache.

useLazyQuery - for cases like yours, when you want to make a call only later (ignoring the render stage) calling the "trigger" on some event (like a button click), and remember about the preferCacheValue option for a case when you have a chance to have the response already cached - this will allow making response feels instantaneous.

In our case - useLazyQuery is MUCH relevant, due to you can avoid all that setCurrentPosition => dispatch => useSelector stuff, and pull all that logic just in click handler, where you'll be able to fetch Geolocation.getCurrentPosition() and pass coordinates to trigger function, that where relevant to the click time, not to the time when the component was rendered.

Cirri answered 13/3, 2022 at 21:20 Comment(1)
Thanks a lot for your answer @Eugene! now I understand the difference :DQuesenberry

© 2022 - 2024 — McMap. All rights reserved.