The debounce function has become a common optimization method to reduce the firing frequency of certain operations.
How does Debounce reduce function calls?
Let’s start with a simple debounce function
function debounce(fn, ms) {
let timer = null;
return (. args) = > {
clearTimeout(timer);
timer = setTimeout(() = > {
fn(...args);
}, ms);
}
}
Copy the code
The idea is to cache timer variables using closures and use clearTimeout to cancel unnecessary operations.
But what does it mean to use debounce properly in a hook
Write a common everyday use of debounce that is’ problematic ‘:
function App() {
const fn = debounce(() = > console.log(1), 500);
return (
<div>
<input onChange={fn} />
</div>
);
}
Copy the code
Does this debounce function work?
.
.
.
The answer is yes, so why is it ok? Let’s take it one step at a time.
Let’s start with the test results
After we put in a couple of ones in a row,
There will only be a 1 by 1 output, which proves that our debounce function is valid.
Requirement – PM needs a button to clear input values with one click
Simply bind value.
function App() {
const [value, setValue] = useState(' ');
const fn = debounce(() = > console.log(1), 500);
return (
<div>
<input
value={value}
onChange={(event)= >{ const _v = event.target.value; setValue(_v); fn(); }} / ><br />
<button onClick={()= >SetValue (' ')} > empty</button>
</div>
);
}
Copy the code
Test steps
- The input
111111
And each interval is 300ms - Click on the empty
- Result Input box has no value
The results of
No problem. Next problem!
but
Did it really turn out that way?
Let’s open console and retry the test steps
There are five ones, no debounce effect, and it looks like a delayed input
❓ ❓ ❓
Instead of adding a state to the debounce function, it’s not an ounce.
Why did this happen — the molesetValue
How does setValue mess up?
Enter 1 for the first time
Input onChange is triggered, event.target.value is 1, setValue is triggered to update the React view, and the fn function is executed
Enter 1 for the second time
React execute App(), debounce(() => console.log(1), 500); Return a new function (note that the function is not the previous fn, but a new fn, fn! == fn), input onChange is triggered, event.target.value is 11, setValue is triggered to update the React view, and then fn function is executed
.
You can actually see it here.
Debounce cashes the timer variable by executing a function, using a closure. However, debounce returns multiple functions multiple times, in which case the same closure is not shared. SetValue triggers the execution of the function, generating a new function with each execution. Fn no longer shares the same closure as the previous fn.
change
As long as debounce returns the same function, we use useCallback to implement this
function App() {
const [value, setValue] = useState(' ');
const fn = useCallback(debounce(() = > console.log(1), 500), []);
return (
<div>
<input
value={value}
onChange={(event)= >{ const _v = event.target.value; setValue(_v); fn(); }} / ><br />
<button onClick={()= >SetValue (' ')} > empty</button>
</div>
);
}
Copy the code
Complete:
But is it okay?
Let’s try to read the value inside of this function.
function App() {
const [value, setValue] = useState(' ');
const fn = useCallback(
debounce(() = > console.log(value), 500),
[]);
return (
<div>
<input
value={value}
onChange={(event)= >{ const _v = event.target.value; setValue(_v); fn(); }} / ><br />
<button onClick={()= >SetValue (' ')} > empty</button>
</div>
);
}
Copy the code
You’ll notice that no matter how you type it, the value is always a null character, which is the initial value of value.
Because useCallback caches this function, the function still retains its own closure.
Let’s do this with useRef
function App() {
const [value, setValue] = useState(' ');
const f = () = > console.log(value);
const fRef = useRef();
fRef.current = f;
const fn = useCallback(
debounce(() = > fRef.current(), 500),
[]);
return (
<div>
<input
value={value}
onChange={(event)= >{ const _v = event.target.value; setValue(_v); fn(); }} / ><br />
<button onClick={()= >SetValue (' ')} > empty</button>
</div>
);
}
Copy the code
The paper comes zhongjue shallow, you can try yourself. You can join me in the discussion
Custom hooks
function useDebounce(fn, ms) {
const fRef = useRef();
fRef.current = fn;
const result = useCallback(
debounce(() = > fRef.current(), ms),
[]);
return result;
}
Copy the code
Voom, this is Uther of the Front Order.
If you need to push inward, if you need to touch fish, plus group plus me ++++js_xiaomayi