22

I've successfully written my application using Axios to fetch content. As of now, it's set up to fetch content when certain events happen (like the submit button has been clicked.) However, I'm experimenting with Redux's RTK-Query solution. This package generates hooks and in their examples, they provide simple component-level examples that call the hooks on mount.

How can I leverage these rtk-hooks (and hooks in general) so I can tie them to behaviors like onClick, onSubmit, and conditional events? I'm aware this conflicts with the rules-of-hooks guidelines, but I can't imagine RTK-Query would be so limited as to only allow component-level onMount API calls.

some related articles I'm reading while I try to figure this out / wait for a helpful example:

The second article seems somewhat relevant but I feel like its beating too far off the path and is making question if it's even worth having rtk-query installed. I might as well just use axios since it can be used anywhere in my components and logic. Can someone educate me on how to approach this problem? I'm new to rtk-query, it seems really cool but it also seems really restrictive in its implementation approaches.

Here is an example of my api.ts slice:

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

const apiEndpoint = 'http://localhost:5000';

export const myApi = createApi({
  reducerPath: 'myApi',
  baseQuery: fetchBaseQuery({ baseUrl: apiEndpoint }),
  endpoints: builder => ({
    getFileById: builder.query<any, { id: string; fileId: string }>({
      query: arg => {
        const { id, fileId } = arg;
        return `/service/${id}/files/${fileId}`;
      },
    }),
  }),
});

// RTK Query will automatically generate hooks for each endpoint query
export const { useGetFileByIdQuery } = myApi;

generic example using axios:

const handleSubmit = async (): Promise<void> => {
    try {
      const getResponse = await axios.get('my-endpoint/abc/123');
    }
    catch (e) {
     ...
    }
}
kevin
  • 2,707
  • 4
  • 26
  • 58

2 Answers2

40

If you use a query, you would use local component state to set the query parameter

import { skipToken } from "@reduxjs/toolkit/query";
const [myState, setState] = useState(skipToken) // initialize with skipToken to skip at first
const result = useMyQuery(myState)

and in your click handler you then set the state

const changePage = () => {
  setState(5)
}

In your case though, you have a form submit and that sounds you want to use a "mutation", not a query. The point of mutations is that you are not really interested in the result long-term, but rather want to trigger a change on the server by sending data.

const [trigger, result] = useMyMutation()

that would be called like

const handleSubmit = () => {
  trigger(someValue)
}
phry
  • 35,762
  • 5
  • 67
  • 81
  • 16
    This is phenomenal, thank you. I want to point out to anyone else reading this and scratching their head, that `skipToken` is an import from RTKQ: `import { skipToken } from "@reduxjs/toolkit/query";` – Jonathan Tuzman Nov 05 '21 at 16:34
  • what is that 5 you've passed to the `setState(5)`? I am no sure 100% but I according to my understanding it is the necessary parameter for the `useMyQuery`. BTW it is a good idea to add a bit more details to your answer. Just 15 people upvoted the previous comment which means that they really did not know that or somehow find it useful. – Kasir Barati May 23 '23 at 23:21
  • @KasirBarati is is whatever value the `myState` variable will become after you called `setState` - the argument to `useMyQuery`. So before, it was called `useMyQuery(skipToken)`, afterwards `useMyQuery(5)`. Since we have not specified what the argument of our endpoint does, it could be whatever. Could be a page number, could be a user id. – phry May 24 '23 at 09:51
  • Thanks for the answer, But right now I have 2 RTK-Query hooks and implemented your solutions and TBH my code is not readable at all. It gets too complicated as soon as you add more RTK-Query hooks. – Kasir Barati May 25 '23 at 07:44
  • 1
    @KasirBarati the initial question asked for one very specific situation. Your situation might actually be quite different - maybe it makes more sense to create your own question to tell more about your specific problem and code? – phry May 25 '23 at 08:17
14

There is another option to pass boolean as the second argument of the query.

For example:

I use this for login:

const [email, setEmail] = useState<string>('')
const [password, setPassword] = useState<string>('')
const [loginCredentials, setLoginCredentials] = useState<ILoginRequest>({ email: '', password: '' })

const { data, error, isError, isSuccess, isLoading } = useLoginQuery(loginCredentials, {
    skip: loginCredentials.email === '' && loginCredentials.password === '',
})

Note: the skip parameter is false when the component load. When user fills email and password I'm setting the email and password state. And when press the login button I set the loginCredentials state and the RTK query is triggered.

Israel
  • 445
  • 2
  • 5
  • 15