0

I have component A where I want to set variable using useQuery instead of useState

function A() {
 let varA= useQuery(['objA'], '');

then inside A I have dropDown list where I set varA using callback function

  const handleDropDownSelect = (e) => {
    varA= e.value;
    }

Till this moment everything works fine. Also the whole app is enclosed in <QueryClientProvider client={queryClient}>

Now I have component B.jsx

function B() {

Where I want to use that varA. I've tried this thing but it won' work.

const queryClient = useQueryClient();
  const varA= queryClient.getQueryData('objA');

I get error

queryCache.ts:171 Uncaught TypeError: Cannot create property 'exact' on string 'objA'

How can I fix this problem ?

David
  • 4,332
  • 13
  • 54
  • 93
  • 1
    Why do you use the same variable for two different things? – Konrad Nov 06 '22 at 21:44
  • @Konrad I don't know, maybe I got it wrong. What I want to do is create variable like useState that can be changed inside component A, and at the same time I want to have access to that variable from component B. And all this using react query. If that is possible in a different way please show your solution. – David Nov 06 '22 at 21:48
  • So what you want is triggering a query to refetch whenever certain value changes? – ivanatias Nov 06 '22 at 23:14

2 Answers2

1

You are not setting anything in the query cache by assigning it to a variable.

The variable will only have the resulted (readonly) useQueryData from the useQuery hook.

function A() {
  const varA = useQuery(['objA'], '');
  const { isLoading, data, error } = varA;

This example below will just overwrite the returned useQueryResult above to the value of the dropdown. A big no no.

function A() {
  let varA = useQuery(['objA'], '');
  
  const handleDropDownSelect = (e) => {
    // will just overwrite varA
    varA = e.value
  }

I think you are looking to use set query data instead of the useQuery hook.

Set data in the cache with setQueryData and retrieve it with getQueryData

const handleDropDownSelect = (e) => {
  queryClient.setQueryData('keyA', e.value);
}

// This should be the value from the dropdown
const result = queryClient.getQueryData('keyA');
Tomas Vancoillie
  • 3,463
  • 2
  • 29
  • 45
  • sorry that is not the right answer. useQuery will put the data into the query cache, but under the key `['objA']`, which is an array with a string. You then cannot retrieve it by trying to pass a string - it needs to be an array as well. – TkDodo Nov 11 '22 at 08:48
1

There is no good or idiomatic way to have one component read data from the cache without knowing the exact query key. It is possible, but it has a lot of drawbacks:

  1. if there are many entries, like ['objA', 'foo'] and ['objA', 'bar'], what would you expect to get when you want to retrieve data for ['objA'] ? It would be a random one from the two.

Note: You can do that, with queryClient.getQueryData(['objA'], { exact: false }). Your mistake was to pass in the string 'objA', not an Array like you have for useQuery. Again, exact: false will yield a random entry if there are more than one... You could also do queryClient.getQueriesData(['objA']). That function is inexact per default and it will return all matching queries.

  1. You're building an implicit dependency between your components. component B will only work if it is rendered as child of component A, and that cannot be enforced. One small refactoring and your data will no be there anymore. This is the bad kind of coupling :)

Now that we know what we should not do, what are good ways to solve this problem:

  1. You can make your dropdown selection global client state. It's a piece of information you need in componentA and componentB, so lift the state up. You can also write it to redux or the url, and then write a custom hook that reads it from there, and use the custom hook in both places. This is the best approach.

  2. You can put the resulting data from componentA into react context, and then have componentB read the data from that context. It will always be up-to-date, but you still have the coupling. But at least the coupling is explicit, not implicit.

TkDodo
  • 20,449
  • 3
  • 50
  • 65