Debouncing and Timeout in React
I don't think React hooks are a good fit for a throttle or debounce function. From what I understand of your question you effectively want to debounce the handleChangeProductName
function.
Here's a simple higher order function you can use to decorate a callback function with to debounce it. If the returned function is invoked again before the timeout expires then the timeout is cleared and reinstantiated. Only when the timeout expires is the decorated function then invoked and passed the arguments.
const debounce = (fn, delay) => {
let timerId;
return (...args) => {
clearTimeout(timerId);
timerId = setTimeout(() => fn(...args), delay);
}
};
Example usage:
export default function ProductInputs({ handleChangeProductName }) {
const debouncedHandler = useCallback(
debounce(handleChangeProductName, 200),
[handleChangeProductName]
);
return (
<TextField
fullWidth
label="Name"
variant="outlined"
size="small"
name="productName"
value={formik.values.productName}
helperText={formik.touched.productName ? formik.errors.productName : ""}
error={formik.touched.productName && Boolean(formik.errors.productName)}
onChange={(e) => {
formik.setFieldValue("productName", e.target.value);
debouncedHandler(e.target.value);
}}
/>
);
}
If possible the parent component passing the handleChangeProductName
callback as a prop should probably handle creating a debounced, memoized handler, but the above should work as well.
How to implement debounce in React.js?
There are some changes which you need to make,
First debouncedOnChangeHandler variable holds the function like,
const debouncedOnChangeHandler = (e) => {
debounce(onChangeHandler(e), 1000);
};
But whereas this doesn't pass the onChangeHandler
as expected argument for debounce
method.
So you need to just passdown the debounce function along with required parameters like,
const debouncedOnChangeHandler = debounce(onChangeHandler, 1000);
Second: You need to remove value={name}
which is not needed in this context.
Forked Codesanbox:
Can someone explain the debounce function in Javascript
The code in the question was altered slightly from the code in the link. In the link, there is a check for (immediate && !timeout)
BEFORE creating a new timout. Having it after causes immediate mode to never fire. I have updated my answer to annotate the working version from the link.
function debounce(func, wait, immediate) {
// 'private' variable for instance
// The returned function will be able to reference this due to closure.
// Each call to the returned function will share this common timer.
var timeout;
// Calling debounce returns a new anonymous function
return function() {
// reference the context and args for the setTimeout function
var context = this,
args = arguments;
// Should the function be called now? If immediate is true
// and not already in a timeout then the answer is: Yes
var callNow = immediate && !timeout;
// This is the basic debounce behaviour where you can call this
// function several times, but it will only execute once
// [before or after imposing a delay].
// Each time the returned function is called, the timer starts over.
clearTimeout(timeout);
// Set the new timeout
timeout = setTimeout(function() {
// Inside the timeout function, clear the timeout variable
// which will let the next execution run when in 'immediate' mode
timeout = null;
// Check if the function already ran with the immediate flag
if (!immediate) {
// Call the original function with apply
// apply lets you define the 'this' object as well as the arguments
// (both captured before setTimeout)
func.apply(context, args);
}
}, wait);
// Immediate mode and no wait timer? Execute the function..
if (callNow) func.apply(context, args);
}
}
/////////////////////////////////
// DEMO:
function onMouseMove(e){
console.clear();
console.log(e.x, e.y);
}
// Define the debounced function
var debouncedMouseMove = debounce(onMouseMove, 50);
// Call the debounced function on every mouse move
window.addEventListener('mousemove', debouncedMouseMove);
How implement debounce for event listener ? Problem with storage event
Well just set the debounced function as event listener?
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
const storageEventCb = () => console.error(1);
// Define the debounced function
const debouncedCb = debounce(storageEventCb, 1500);
window.addEventListener('storage', debouncedCb);
Debounce function issue
The debounce function returns another function, which you can think of like an object of a class (it has a "memory" of timer, which is setTimeout id), and you always invoke the same function created with debounce() function. But in the first example, you create new debounced function on every input event, which creates new object unaware of previous timeouts.
Related Topics
Shell Tool Which Renders Web Site Including JavaScript
Npm Not Installing Returns Error Consistently
Simulating Linux Terminal in Browser
How to Install Jslint on Ubuntu
Rails 4: Disable Turbolinks in a Specific Page
Is "Monkey Patching" Really That Bad
Rails 4 Turbolinks Make Form Submit Multiple Times
How to Execute JavaScript in Ruby Written Webdriver Test
Problem Deploying Rails 3.1 Project to Heroku: Could Not Find a JavaScript Runtime
Rails: Post 422 (Unprocessable Entity) in Rails? Due to the Routes or the Controller
Ruby's ||= (Or Equals) in JavaScript
Create an Array of Characters from Specified Range
How to Use a Ruby Loop Inside of Haml's :JavaScript Region
Scoping the Results for Rails3 Jquery Autocomplete Plugin
Inject a JavaScript Code in Webview iOS