Fork me on GitHub
#re-frame
<
2023-09-21
>
Henrique Prange21:09:29

Hey guys! I’m working on a typeahead component that filters a list as the user types. Each letter triggers a request for filtered results from the server, with debounce to avoid too many requests. However, occasionally, multiple requests are still sent. If the first request is slow (like in the diagram), our app displays outdated results. What’s the best way to prevent the first request from overriding the second request’s result?

Omar22:09:37

I had a somewhat similar situation at one point and my lazy hack was to store the last request and abort it if a new one were to take place. Not the best, but it works.

p-himik22:09:24

Three solutions that I know of: • The mentioned above, with canceling requests (I assume "not the best" means that the implementation wasn't the best because the approach itself is IMO actually the best) • Track the ordinal of the request and only respect the results of the request with the right ordinal • Same but track input arguments instead

Henrique Prange22:09:57

Got it! Since I’m using re-frame-http-fx, I think I can use the :on-request handler to track requests (as explained in the link below), and if a new request comes in, I can call the abort method on the previous one. Does that sound right? https://github.com/day8/re-frame-http-fx#optional-handler-for-on-request

p-himik22:09:57

Yeah. You just have to make sure that you're aborting requests for that specific component for that specific task and not something else.

Omar22:09:24

depending on the result size you send back, I'd personally probably store each request and match that up with the current input, then clear all the results on blur or selection or whatever. that way when a user backspaces, if the results were there it'd show immediately.

👍 1
Henrique Prange22:09:35

Sounds like a good idea. Thanks!

Henrique Prange22:09:15

Just curious, how would you track the order of requests in the second approach? I was thinking of using a timestamp to see if the response is too old and should be ignored. Is that what you had in mind?

Henrique Prange22:09:53

I mean, the “Track the ordinal of the request and only respect the results of the request with the right ordinal” option.

Omar22:09:44

Something like this would be really easy to deal with:

{:autocomplete {"a" [<results>]
                "abc" [<results>]}}

p-himik22:09:08

@U029WR2TAV6 Nah, just have an integer value somewhere and only increment it whenever a new request comes in. The idea is stolen from here, where that integer is named "epoch": https://lucywang000.github.io/clj-statecharts/docs/integration/re-frame/#discard-stale-events-with-epoch-support

p-himik22:09:41

But for typeahead, I actually like Omar's solution with storing the results.

Henrique Prange22:09:23

Yeah, it seems like a solid choice, especially because we can cache the results if the user goes back and edits their input.

p-himik22:09:37

I'd just make sure that it's a limited LRU cache or something similar.

Henrique Prange22:09:34

Got it! Now I’ve got what I need to make an informed decision. Thanks for the assistance, folks!

👍 1
DrLjótsson18:09:41

In the event that dispatches the http effect, you increase a counter in app-db and you include that counter in the event vector that is dispatches when the http event completes. If the count in that event vector and app-db match, you use the returned value, otherwise discard it. EDIT: Sorry, I hadn’t read the whole thread (forgot to scroll 🤯 ), I was describing the same solution that @U2FRKM4TW linked to.

👍 2