Hotwire Discussion

_.throttle inside a controller

Anyone know how to use _.throttle inside a controller? I’ve tried it, but it tells me the method I’m trying to throttle isn’t a function. It kind of feels like there should be some way to throttle baked into the event handling of Stimulus. If I’m using a keyup event, I want to have a little delay when firing the POST off to the server.

You can throttle an action method in initialize:

import { Controller } from "stimulus"

export default class extends Controller {
  initialize() {
    this.pressKey = _.throttle(this.pressKey, 100)
  }

  pressKey(event) {
    console.log(`Pressed key: ${event.char}`)
  }
}

Alternatively, define a function decorator that does this for you:

import { Controller } from "stimulus"

function throttle(wait) {
  return function (target, name, descriptor) {
    const { value } = descriptor

    if (typeof value === "function") {
      descriptor.value = _.throttle(value, wait)
    }

    return descriptor
  }
}

export default class extends Controller {
  @throttle(100)
  pressKey(event) {
    console.log(`Pressed key: ${event.char}`)
  }
}

You can move the throttle decorator to a different file and import it where you need it. (Note that to take the decorator approach, you’ll need to install a transpiler plugin such as the transform-decorators plugin for Babel.)

1 Like

Excellent, I used the initializer approach. Thanks for the info!

I’m having an issue along the same thread. I’m trying to run this:

initialize() {
  window.addEventListener('scroll', _.throttle(this.isInView, 500))
}

isInView() {
  console.log(this.element);
}

However this.element is not available in my function isInView() for some reason. It returns ‘undefined’ in the console. Can’t figure out why. Thanks for the help in advance!

I’m guessing that this is referring to the lodash function and not your stimulus class. Try adding this to your controller near the top of the file:

import { Controller } from 'stimulus'
let throttle = require('lodash/throttle');

Then call:
throttle(this.isInView, 500) instead of _.throttle(this.isInView, 500).

initialize() {
  window.addEventListener('scroll', throttle(this.isInView, 500))
}

isInView() {
  console.log(this.element);
}