dknutsen / ember-needs-async

Docs: https://dknutsen.github.io/ember-needs-async/
MIT License
8 stars 4 forks source link

Debounce async request #37

Open allthesignals opened 5 years ago

allthesignals commented 5 years ago

This addon is real gem - thank you for making it.

I started my own "data-store" component, which had a yield timeout(100) inside the restartable task. This is because the fetch is requested on mouse hover, which is too many requests at once. Hence, debounce.

Do you have any example of using your API to compose a similar behavior? I cannot quite see how the pieces would fit together.

I tried this, but I don't think that's what it's for:

        {{#needs-async
          needs=(async-all
            (array (timeout 100)
            (find-record 'modal-split' features.firstObject.properties.geoid))
          )
          as |states|
        }}
       {{/needs-async}}

Happy to add more to the examples section of the docs!

dknutsen commented 5 years ago

Hmmmm very interesting 🤔 how would you connect this to the on-hover event? Like is this block already getting conditionally rendered on hover somehow? Basically this should only make a request when it is rendered, so if you have issues with too many requests I'd think you'd also have issues with too many renders which isn't great either.

dknutsen commented 5 years ago

I guess I'd follow up on that by saying that (and I'm making some assumptions here so forgive me if this is off base) if you're rendering some sort of modal or popup (I'm inferring that mostly based on the record type) while hovering over something else, and you want that modal/popup to load a record using the snippet you posted above, I'd think you'd want to handle the "debounce" situation more "upstream".

My inclination would be to do something like:

  1. on mouseenter event for "thing that gets hovered on" you set a flag to show modal/popup to true, let's call it showModal. So on mouseenter showModal -> true
  2. on mouseleave event for "thing that gets hovered on" you set showModal to false
  3. render the modal/popup something like this:

    {{#if showModal}}
    {{#modal-wrapper}}
    {{#needs-async
      needs=(find-record 'modal-split' features.firstObject.properties.geoid)
      as |states|
    }}
    
      {{!-- modal content here --}}
    
    {{/needs-async}}
    {{/modal-wrapper}}
    {{/if}}

Again I'm making a lot of assumptions but like I said if you're seeing a ton of data requests you're probably also seeing a ton of rerenders which is also something you'd want to avoid. If you actually do want rerenders for whatever reason maybe consider turning background refresh off for that model type so a findRecord only makes the one request and then pulls from the store on consecutive fetches? There are other ways you could handle it too, like writing a custom helper or something...

allthesignals commented 5 years ago

@dknutsen thank you! I went ahead and composed stuff, adding what I needed on top of what you created :) here's what I got:

{{#with (debounce intersectingFeatures.firstObject.properties.geoid 400) as |geoIdTask|}}
  {{#needs-async needs=(find-record 'modal-split' geoIdTask.value) as |modalData|}}
    {{#modalData.loading}}
      <i class="spinner icon"></i>
    {{/modalData.loading}}
    {{#modalData.loaded as |data|}}
      {{data.count}}!
    {{/modalData.loaded}}
  {{/needs-async}}
{{/with}}

how would you connect this to the on-hover event? Like is this block already getting conditionally rendered on hover somehow? Basically this should only make a request when it is rendered, so if you have issues with too many requests I'd think you'd also have issues with too many renders which isn't great either.

Yup, it's yielded out from a contextual component, and gets passed into a UI card:

{{#mapbox/current-mouse-position map=map as |mapMouseEvent|}}
  {{#mapbox/intersecting-features map=map
    point=mapMouseEvent.point
    options=(hash layers=(array layerId)) as |intersectingFeatures|
  }}
    {{#if (and mapMouseEvent.point intersectingFeatures)}}
      {{#hover-card point=mapMouseEvent.point}}
{{!-- UI and Needs Async stuff here !}}

I think you're right about too many renders... I don't know how to address that, but I have it setup so that the hover UI box follows the mouse around when the mapMouseEvent gets updated. Separately, "modal" here just refers to "mode of transportation", not the UI concept.

I should probably group some of this together more but here's the full extent of where I landed:

{{#mapbox/current-mouse-position map=map as |mapMouseEvent|}}
  {{#mapbox/intersecting-features map=map
    point=mapMouseEvent.point
    options=(hash layers=(array layerId)) as |intersectingFeatures|
  }}
    {{#if (and mapMouseEvent.point intersectingFeatures)}}
      {{#hover-card point=mapMouseEvent.point}}
        <div class="ui card">
          <div class="content">
            <div class="header">{{intersectingFeatures.firstObject.properties.boroname}}</div>
            <div class="meta">
              <span>Census Tract ID: {{intersectingFeatures.firstObject.properties.geoid}}</span>
            </div>
            <div class="description">
              {{#with (debounce intersectingFeatures.firstObject.properties.geoid 400) as |geoIdTask|}}
                {{#needs-async needs=(find-record 'modal-split' geoIdTask.value) as |modalData|}}
                  {{#modalData.loading}}
                    <i class="spinner icon"></i>
                  {{/modalData.loading}}
                  {{#modalData.loaded as |data|}}
                    <div class="header">{{data.count}}</div>
                    <div class="meta">
                      <span>2 days ago</span>
                      <a>{{data.mode}}</a>
                    </div>
                    <p></p>
                  {{/modalData.loaded}}
                {{/needs-async}}
              {{/with}}
            </div>
          </div>
        </div>
      {{/hover-card}}
    {{/if}}
  {{/mapbox/intersecting-features}}
{{/mapbox/current-mouse-position}}

Here's some animation - it's not perfect, it does flicker a little bit, and I think I have some things a little out of order (it should start loading as soon as there's a settled ID to query for):

Anyways, this is super interesting! Thanks for the thoughtful, thorough response.

dknutsen commented 5 years ago

Oh that's awesome. I always love maps and geo data. Thanks for sharing! And glad you got something working. Definitely a trickier scenario if the modal has to move with the mouse but it looks like you're on the right track. Good luck! And definitely let me know if you have any ideas for the addon or examples while you're hacking around.