Debounce
Debounce is intended to eliminate jitter, and for scenarios where events fire frequently, only the last programmatically controlled event is valid.
The anti-shake function, what we need to do is to set a timer when something is triggered so that the event will be delayed. During the timer period, if the event is triggered again, the timer will be cleared and reset. Until the timer is still not cleared, the event will actually happen.
const debounce = (fun, delay) = > {
let timer;
return (. params) = > {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() = >{ fun(... params); }, delay); }; };Copy the code
If an event causes a variable to change frequently, using Debounce can reduce the number of changes. Get a new modify function to use by passing in the modify function.
For class components, new functions can be mounted to this, but the function component local variables will be created every time render is created, and debounce will lose its effect. In this case, it is not convenient to save member functions by using useRef. This made it necessary to make debounce a hook.
function useDebounceHook(value, delay) {
const [debounceValue, setDebounceValue] = useState(value);
useEffect(() = > {
let timer = setTimeout(() = > setDebounceValue(value), delay);
return () = > clearTimeout(timer);
}, [value, delay]);
return debounceValue;
}
Copy the code
In a functional component, the target variable can be converted once by useDebounceHook, which is fired only after delay is satisfied, and any firing during delay resets the timer.
With the useEffect, actions are not performed until the Debounce value has changed. The following text state changes frequently, but relies on debounceText, so the useEffect callback is triggered after the specified delay.
const [text,setText]=useState(' ');
const debounceText = useDebounceHook(text, 2000);
useEffect(() = > {
// ...
console.info("change", debounceText);
}, [debounceText]);
function onChange(evt){
setText(evt.target.value)
}
Copy the code
In the above search box, the search request is triggered only after 1 second (specified delay) is completed, which has achieved the purpose of anti-shock.
Throttle
Throttle is another strategy that can trigger only once in a period of time when events are frequently triggered.
Throttling functions are used in scenarios where events are triggered more frequently than stabilization functions, sliding events, scrolling events, and animations.
Take a look at a general throttling function (ES6) :
function throttleES6(fn, duration) {
let flag = true;
let funtimer;
return function () {
if (flag) {
flag = false;
setTimeout(() = > {
flag = true; }, duration); fn(... arguments);// fn.call(this, ... arguments);
// fn.apply(this, arguments); // This is the App Component at runtime, and the function runs in the App Component
} else {
clearTimeout(funtimer);
funtimer = setTimeout(() = > {
fn.apply(this.arguments); }, duration); }}; }Copy the code
(use… Arguments are the same as call expansion arguments and apply arguments.)
Extension: Prior to ES6, there were no arrow functions, and you needed to manually retain the this and argument in the closure function and pass in the timer function call:
So, the common ES5 version of the throttling function:
function throttleES5(fn, duration) { let flag = true; let funtimer; return function () { let context = this, args = arguments; if (flag) { flag = false; setTimeout(function () { flag = true; }, duration); fn.apply(context, args); // Hold this and arguments for the upper level function } else { clearTimeout(funtimer); funtimer = setTimeout(function () { fn.apply(context, args); }, duration); }}; }Copy the code
How to make the throttling function a custom Hooks as well? The above anti-shaking Hook is actually anti-shaking of a variable. From a variable that changes constantly, a variable can be changed according to the rule (after the change delay time is stopped). We throttle the change of a variable, that is, from a variable that changes frequently without interruption to a variable that can change only once during the specified duration (and will change after it ends).
Hook implementation for throttle:
(Flag variables that indicate whether a function can be called with a changed value are stored in the closure environment in regular functions and in the Hook by useRef)
function useThrottleValue(value, duration) {
const [throttleValue, setThrottleValue] = useState(value);
let Local = useRef({ flag: true }).current;
useEffect(() = > {
let timer;
if (Local.flag) {
Local.flag = false;
setThrottleValue(value);
setTimeout(() = > (Local.flag = true), duration);
} else {
timer = setTimeout(() = > setThrottleValue(value), duration);
}
return () = > clearTimeout(timer);
}, [value, duration, Local]);
return throttleValue;
}
Copy the code
Corresponding use in gesture sliding:
export default function App() {
const [yvalue, setYValue] = useState(0);
const throttleValue = useThrottleValue(yvalue, 1000);
useEffect(() = > {
console.info("change", throttleValue);
}, [throttleValue]);
function onMoving(event, tag) {
const touchY = event.touches[0].pageY;
setYValue(touchY);
}
return (
<div
onTouchMove={onMoving}
style={{ width: 200.height: 200.backgroundColor: "#a00}} "/ >
);
}
Copy the code
Thus, the yValue of the gesture keeps changing, but since throttleValue is used, the useEffect callback is throttled in accordance with the rule, and can only be executed once per second, and last time after stopping the change for one second.
Control over values or functions
In the first instance, text updates state as it is typed, but useEffect is implemented according to the useEffect rule because it depends on the useEffect value.
Can you advance this anti – shake rule? Updating state in advance is in accordance with the anti-shake rule, that is, the new value can be setState only after the delay is specified. Of course, it is feasible. But the search box example here is not good. You can throttle requests after the value changes, but because the search box needs to display the input in real time, you need the real time text value.
Duration can be set to control the frequency, and the setState of the gesture value can only be set once per second:
export default function App() {
const [yvalue, setYValue] = useState(0);
const Local = useRef({ newMoving: throttleFun(setYValue, 1000) }).current;
useEffect(() = > {
console.info("change", yvalue);
}, [yvalue]);
function onMoving(event, tag) {
const touchY = event.touches[0].pageY;
Local.newMoving(touchY);
}
return (
<div
onTouchMove={onMoving}
style={{ width: 200.height: 200.backgroundColor: "#a00}} "/ >
);
}
// Regular throttling function
function throttleFun(fn, duration) {
let flag = true;
let funtimer;
return function () {
if (flag) {
flag = false;
setTimeout(() = > (flag = true), duration); fn(... arguments); }else {
clearTimeout(funtimer);
funtimer = setTimeout(() = > fn.apply(this.arguments), duration); }}; }Copy the code
This is where you control the function, the frequency of the setYValue function, pass the setYValue function into the throttling function, get a new function, use the new function in the gesture event, and then the call to the setYValue conforms to the throttling rule. There would be a lot of unnecessary setYValue execution if the gesture value was still throttled, so it would be better to throttle the setYValue function.
Note that the resulting new function needs to be temporarily stored with useRef as an “instance variable”, otherwise it will be recreated for each render execution of the function component.