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;