Functional programming's higher order method
Ref:
https://betterprogramming.pub/how-to-allow-only-one-click-events-in-javascript-72938027fbf5
Gist:
const once = (f) => {
  let finished = false;
  return (...args) => {
    if (!finished) {
      finished = true;
      f(...args);
    }
  };
};
Complete example:
- clicker 0 will fire events on every click.
- this is the problematic one.
 
- this can cause extra triggers to server-side if the network is slow and the user clicks again hoping the data will be submitted faster.
 
 
- clicker 1 will only fire again, after the button itself and the click function ref is re-rendered.
- this is a simple solution if we have to re-render the main component after the server has responded.
 
 
- clicker 2 will only fire again when value2 has any changes.
- if we want to control using specific state
 
 
import "./App.css";
import { useState, useCallback } from "react";
const once = (f) => {
  let finished = false;
  return (...args) => {
    if (!finished) {
      finished = true;
      f(...args);
    }
  };
};
function App() {
  const [value1, setValue1] = useState("value1");
  const [value2, setValue2] = useState("value2");
  console.log(`app rendered`);
  const onChange1 = useCallback((e) => {
    console.log(`onChange1`, e.target.value);
    setValue1(e.target.value);
  }, []);
  const onChange2 = useCallback((e) => {
    console.log(`onChange2`, e.target.value);
    setValue2(e.target.value);
  }, []);
  const onClick0 = () => {
    // mocking 2 secs network request
    setTimeout(() => {
      // set value to value1 to cause the re-render
      setValue1(new Date());
      console.log("clicker 0");
    }, 2000);
  };
  const onClick1 = () => {
    // mocking 2 secs network request
    setTimeout(() => {
      // set value to value1 to cause the re-render
      setValue1(new Date());
      console.log("clicker 1");
    }, 2000);
  };
  const onClick2 = () => {
    // mocking 2 secs network request
    setTimeout(() => {
      // set value to value1 to cause the re-render
      setValue1(new Date());
      console.log("clicker 2");
    }, 2000);
  };
  const memoOnceOnClick2 = useCallback(once(onClick2), [value2]);
  return (
    <div className="App">
      <header className="App-header">
        <input value={value1} onChange={onChange1} />
        <input value={value2} onChange={onChange2} />
        <button onClick={onClick0}>
          clicker 0 / run every time i am clicked
        </button>
        <button onClick={once(onClick1)}>
          clicker 1 / run once until i am re-render
        </button>
        <button onClick={memoOnceOnClick2}>
          clicker 2 / run once until value2 is changed
        </button>
      </header>
    </div>
  );
}
export default App;