Search code examples
javascriptionic-frameworkreact-hooksunderscore.jsdebouncing

Underscore's debounce seemingly not taking effect with function calling an async function


I have some ionic segment buttons that essentially act as toggle switches. I'm trying to limit the rate at which the effects of the toggles can be switched/selected) and have tried Debounce & Throttle from both lodash and now underscores without success.

I'm currently using underscores and am still having the issue that the function is still being called freely despite the use of debounce.

Here's my code:

const SensorMeasurements: React.FC = () => {
//.....


const setOptionsHandler = (value: string, option: string, key: string) => {
if (value === "true") {
  console.log("RESULT: True / " + value + " / " + option + " / " + key);
  if (!settingsHandler(key)) {
    setControlHandler(option, false); //toggle on
    console.log("TOGGLE " + option + " ON!");
  }
}
else { // false
  console.log("RESULT: False / " + value + " / " + option + " / " + key);
    if (settingsHandler(key)) {
    setControlHandler(option, false); //toggle off
    console.log("TOGGLE " + option + " OFF!");
    }
  }
}

const setOptionsHandlerThrottled = _.debounce(setOptionsHandler, 5000, true);

const setControlHandler = async (input: string, notify?: boolean) => {
    let sysCommand = "";
    switch (input) {
      case "Reset":
        sysCommand = "R";
        break;
      case "Test":
        sysCommand = "T";
        break;
      case "Calibration":
        sysCommand = "B";
        break;
      case "Logging":
        sysCommand = "L";
        break;
      case "Trigger":
        sysCommand = "H";
        break;
      case "Measuring":
        sysCommand = "M";
        break;
      case "Unit":
        sysCommand = "U";
        break;
    }
    try {
      const abControl = str2ab(sysCommand);
      const dvControl = new DataView(abControl.buffer);

      //console.log("CONTROL BUFFER", abControl);

      await BleClient.initialize();

      //if (isAndroid && (devices.deviceId ?? true)) {
      //  await BleClient.getDevices(devices.deviceId);
      //  await BleClient.disconnect(devices.deviceId);
      //}

      await BleClient.getDevices(devices.deviceId);
      await BleClient.connect(devices.deviceId, (deviceId) => onDisconnect(deviceId));
      await BleClient.write(devices.deviceId, SERV_SYSTEM_CONTROL, CHAR_OPERATIONAL_MODE, dvControl);

      if (notify !== false) {
        present(input + ' Command Sent.', 3000);
      }

    } catch (error) {
      CatchError(error, "Disconnected");
    }
  }

  const settingsHandler = (string: string) => {
    return trueOrFalse(iTrueStates, string) as unknown as string;
  }

 const trueOrFalse = (array: string[], string: string) => {
   //check if string is in the array - returns boolean
   return array.includes(string);
 }

return (   
 <IonSegment onIonChange={e => setOptionsHandlerThrottled(e.detail.value as string, "Trigger", "statusSensorHighTrigger")} className="lowercase" color="brand" value={settingsHandler("statusSensorHighTrigger")}>
      <IonSegmentButton value="false">
        <IonLabel>Low</IonLabel>
      </IonSegmentButton>
      <IonSegmentButton value="true">
        <IonLabel>High</IonLabel>
      </IonSegmentButton>
    </IonSegment>
);
export default SensorMeasurements;

Why is setOptionsHandler still being called when the IonSegmentButton changes regardless of the debounce timeout? Am I accidentally creating a new debounced function every time it's called?


Solution

  • Ultimately I ended up using AwesomeDebouncePromise as this worked best. In order to prevent the creation of a new Debounced function every rerender I wrapped the debounced function in a useMemo.

    const setOptionsHandlerThrottled = useMemo( () => AwesomeDebouncePromise(setOptionsHandler, 3000), [iTrueStates] );