Search code examples
reduxreact-reduxredux-toolkit

RTK Prevent matchFulfilled from being called if there is a more recent request


I am working on an web app with multiple cards. When a card is selected some data will be loaded on a map. Some cards might load slower than other (for some of them there is more data and more calculations involved).

The issue is that if a card with more data is selected, and afterwards the user selects another card with less data (while the request for the first one is still loading) there might be situations where the request for the second card loads faster than the first one. So basically the data for the second card one is loaded for a short time on the map, and afterwards, when the first request also finishes loading, the data on the map replaced with information from the first card (even tough the second one is the current selected one).

I tried aborting the request when another one is fired. Something like:

const req = getCardData(id)
...
req.abort()

But the request still finishes normally and getCardData.matchFulfilled is still called.

I thought about trying to check if the data loaded is for the current selected card, but this is really problematic especially since it can happen for different requests (the card loading part might happen in different parts of the app and some additional requests might get triggered after the first one is done).

Also I might not have the data available for all of them because some of the request are calculated of FE side and for those I don't really have the id available. Making it available would require quite some work and just storing and passing over a lot of functions and components an id for the sole purpose of checking if the last request response is the last triggered, so I'd really like another approach if there is any.

I also read this question (and answers) as well as this github issue but it doesn't seem to give a good option for a lazy query. I am open to other alternatives if aborting isn't an option, but I would appreciate a generic one (if possible)

Thank you in advice!


Solution

  • Came to a conclusion on github with a some help. Ended up using something like:

    let lastRequestId: number
    export const mySlice = createSlice({
      ...
      extraReducers: (builder) => {
        builder.addMatcher(myApi.endpoints.getData.matchPending, (state, action) => {
          lastRequestId = action.meta.requestId
        })
        builder.addMatcher(myApi.endpoints.getData.matchFulfilled, (state, action) => {
           if(lastRequestId == action.meta.requestId)  {
             ...
           }
        })
    

    For reference here is the discussion on github


    Another option might be to use your state to check if the request is the one you wanted.

    For example let's say you have a list of movies, selectedMovie in your state and you requested the list of movies for selectedMovie.id (something like getMoovies(moovie.id) ).

    For this case you can do something like:

    export const mySlice = createSlice({
      ...
      extraReducers: (builder) => {
        builder.addMatcher(myApi.endpoints.getData.matchPending, (state, action) => {
          // originalArgs are arguments passed to the query
            const requestId = Number(action.meta.arg.originalArgs)
            if (state.selectedMoovie.id === requestId) {
              // only update the state in this case
            }
          }
        },
        ...
      }
    })
    

    For additional information on RTK useQuerry args (and how to use originalArgs) you can check RTK useQuerry docs