admin管理员组

文章数量:1410730

I am having issues understanding how to get useLazyQuery to get the cached data on a second time the component is mounted.

Imagine the following:

...
  const [trigger, { data }] = api.useLazyQuery(); // assume it takes 10 seconds to run
  const exists: boolean = !!data;
  const onClickHandler = (e) => { trigger(e.target.value) }; // handler for a button that takes an argument

  React.useEffect(() => {
    console.log(data);  // it shows undefined
  }, [data]);

  return <>{exists && <>Result data: {data}</>}</>
...

So basically, the first time the component is mounted, the user clicks a button to trigger the run of api.useLazyQuery(), get the data and show the results after 10 seconds. Good.

Now on the second time the component is mounted, it should just simply show the data. It doesn't because data is undefined. But if the user clicks the button, it shows the results right away, it didn't wait for those 10 seconds. This means that the data was returned from the store/cache.

So how do I go about doing this so that I can immediately show the data after the second time the component is mounted, without having the user clicking the button just to trigger the trigger function?


Edit: updating the example to reflect the fact that trigger takes an input parameter. There is a proposal to use fixedCacheKey but that essentially keeps the same cache for the same endpoint, regardless of the input argument.

I am also open to other solutions if there is no way around the rtk.

I am having issues understanding how to get useLazyQuery to get the cached data on a second time the component is mounted.

Imagine the following:

...
  const [trigger, { data }] = api.useLazyQuery(); // assume it takes 10 seconds to run
  const exists: boolean = !!data;
  const onClickHandler = (e) => { trigger(e.target.value) }; // handler for a button that takes an argument

  React.useEffect(() => {
    console.log(data);  // it shows undefined
  }, [data]);

  return <>{exists && <>Result data: {data}</>}</>
...

So basically, the first time the component is mounted, the user clicks a button to trigger the run of api.useLazyQuery(), get the data and show the results after 10 seconds. Good.

Now on the second time the component is mounted, it should just simply show the data. It doesn't because data is undefined. But if the user clicks the button, it shows the results right away, it didn't wait for those 10 seconds. This means that the data was returned from the store/cache.

So how do I go about doing this so that I can immediately show the data after the second time the component is mounted, without having the user clicking the button just to trigger the trigger function?


Edit: updating the example to reflect the fact that trigger takes an input parameter. There is a proposal to use fixedCacheKey but that essentially keeps the same cache for the same endpoint, regardless of the input argument.

I am also open to other solutions if there is no way around the rtk.

Share edited Mar 8 at 21:38 renatodamas asked Mar 7 at 22:09 renatodamasrenatodamas 19.7k8 gold badges41 silver badges59 bronze badges 1
  • This is normal, isn't it? When a user action triggers a data query – whether fetching from the server or retrieving cached data – the component naturally reinitializes. If you want the component to retain its state when revisited (preserving its appearance from the first exit), you should cache the component. – yuanyxh Commented Mar 8 at 3:54
Add a comment  | 

1 Answer 1

Reset to default 1

Issue

The issue you are observing is due to the fact that when the component remounts it is getting a new "instance" of the lazy query hook and trigger function. The query endpoint data is cached in the store, but the subscription is cleared when the component unmounts, or rather, a new subscription will be created the first time the trigger function is called. This is when the subscription and cached data are coupled.

Solution Suggestion

  1. You can accept this as the default expected behavior of query endpoints and lazy hooks and be done with it and move on.
  2. You can use a mutation endpoint and a fixed cache key so any cached data will be immediately available to any consumers.

Example:

Update from a builder.query to builder.mutation endpoint definition:

import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

export const api = createApi({
  baseQuery: fetchBaseQuery({
    baseUrl: "https://jsonplaceholder.typicode/",
  }),
  endpoints: (build) => ({
    getPosts: build.mutation({ // <-- mutation
      query: () => "posts",
    }),
  }),
});

export const { useGetPostsMutation } = api;

Call the mutation hooks and provide a fixedCacheKey value so all instances of that hook will access the same cached endpoint data.

import { useGetPostsMutation } from "./api.slice";
import { useEffect, useState } from "react";

const Component = () => {
  const [trigger, { data }] = useGetPostsMutation({
    fixedCacheKey: "keep" // *
  });

  useEffect(() => {
    console.log({ data });
  }, [data]);

  const clickHandler = () => {
    trigger();
  };

  return (
    <>
      <button type="button" onClick={clickHandler}>
        Trigger Query
      </button>
      <div>Data: {....data...}</div>
    </>
  );
};

*Note: I used "keep", but any sufficiently unique cache key value can be used.

Demo

本文标签: reactjsHow to get cached data in RTKQuery using useLazyQueryStack Overflow