rtk-incubator / rtk-query

Data fetching and caching addon for Redux Toolkit
https://redux-toolkit.js.org/rtk-query/overview
MIT License
626 stars 31 forks source link

Access raw endpoint promise method #230

Closed mtodberg closed 3 years ago

mtodberg commented 3 years ago

I'm in the process of investigating if rtk-q fits into our large scale application. We are already using redux-toolkit and redux-saga. The sagas are for complicated async logic, in which we sometimes execute regular api requests. With rtk-q consolidating the endpoints in a single abstract structure, how can I perform the raw ajax request in a saga with something that returns a promise? Afaik, there are only the generated hooks and the thunk actioncreator way (initiate)?

I want to do something like this in a saga:

const response = yield put(???); // Takes in a function that returns a promise.
phryneas commented 3 years ago

Yielding the initiate thunk will return a Promise (even puting it in saga should do that), but keep in mind that that promise just returns the state after the thunk execution, no information if that query actually ran. RTK-Q might decide that there is already a request in flight or that the state is just not stale (this can be skipped by forceRefetch: true) and return immediately.

Edit: reworded "after the query" to "after the execution", since that Promise can even return before the first value is in the state - if it is fired while the first query is still running.

mtodberg commented 3 years ago

So in other words there are no way to bypass the rtk-q mechanisms? I noticed that I could do the following, but it's too much of a hack for me:

const response = yield put(fooApi.endpoints.foo.initiate(payload) as any); <-- has to be cast to any
const result = yield response; <-- returns raw api response
phryneas commented 3 years ago

So in other words there are no way to bypass the rtk-q mechanisms?

Is your question right now "there is no way to use this while skipping over it's complete implementation?"?

Generally:

If put does not accept thunks, you have to type it accordingly to be aware of your middleware - just as you need to type dispatch accordingly to be aware of the thunk middleware. Apart from the as any, this is not a hack.

But I have to reiterate: Your code might return before the query has run for the first time, if you trigger it a second time while the exact same query with the same arguments is already running.

Better would probably to wait for the next completed a completion, so

// initiate query
const response = yield put(fooApi.endpoints.foo.initiate(payload), { forceRefetch: true }) 
// wait for finish
yield take(api.endpoints.foo.isFulfilled(payload))
// get result
const result = yield select(api.endpoints.foo.select(payload)) 

But generally, I'm not sure if you're really gonna be happy doing this - these APIs are not really meant to be used in an iterative way.

mtodberg commented 3 years ago

Your code example clears things up for me, thanks!

mtodberg commented 3 years ago

@phryneas FYI, created an issue in the redux-toolkit repository as this basically another, yet highly related question when typing redux-saga's put effect.

mtodberg commented 3 years ago

@phryneas Seems like there is no api.endpoints.foo.isFulfilled. How do you wait for the ongoing request to finish?

mtodberg commented 3 years ago

Like this maybe?

// initiate query and wait for finish 
yield yield put(fooApi.endpoints.foo.initiate(payload), { forceRefetch: true });
// get the result
const result = yield select(api.endpoints.foo.select(payload)) 
phryneas commented 3 years ago

As I said, waiting for the returned promise will under some circumstances return before the fetch has finished. This API is not designed to be used in an imperative way.

phryneas commented 3 years ago

It is meant for declarative consumption. It will at all times give you consistent data, but the timing should not be of your concern.

But to answer your question above, it is matchFulfilled. See Docs: https://deploy-preview-1016--redux-starter-kit-docs.netlify.app/api/rtk-query/created-api/endpoints#matchers