-
Notifications
You must be signed in to change notification settings - Fork 46k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Debounce and onChange #1360
Comments
You have a bigger issue that you can't workaround in that scenario, not updating the value of the input right away will cause React to actively revert to the old value (as if it was read-only), thus when you later update the value the cursor will reset and always be at the end. Why do you need to debounce |
solution: http://jsfiddle.net/Aetet/a6RUP/ |
UPDATE: This is all wrong :) See #1360 (comment)
That's what I would have thought too, but that doesn't actually seem to be the case.
It seems like a common thing to me. For example, re-rendering a preview of an embedded video when changing the URL: Granted, in that specific scenario, most people will copy/paste rather than typing, but it would be pretty awful performance for anyone who manually typed it, or who wanted to trim out some extra parts that were accidentally copied, etc. It's also important to keep in mind that a huge segment of people around the world aren't working on powerful computers like many developers are. People shouldn't have to be rich to have a smooth experience.
Can you elaborate on that? |
In your scenario you wouldn't need to debounce For example your code could look like constructor(props) {
super(props);
this.state = {
inputText: '',
currentUrl: ''
};
this.handleChange = this.handleChange.bind(this);
this.updateUrl = this.updateUrl.bind(this);
// Debounce
this.updateUrl = _.debounce(this.updateUrl, 500);
}
handleChange(e) {
this.setState({
inputText: e.target.value
});
// It's debounced!
this.updateUrl();
}
updateUrl() {
this.setState({
currentUrl: this.state.inputText
});
}
render() {
return (
<div>
<input value={this.state.inputText} />
<MyVideo url={this.state.currentUrl} />
</div>
);
} This way the text updates happen synchronously as expected, but the video is updated when necessary. |
Ah, yeah, I was conflating debouncing the And I also mistakenly thought that the input field I was working with was controlled when it's actually uncontrolled. ( It's wrapped in a component, so I didn't notice until I opened that and looked at it ). That's why I was thinking that debouncing a controlled field wouldn't interfere with its value being updated. My bad, thanks for the reply :) |
@gaearon Cool, but you code tries to change state after component unmounted 😄 |
Found a good solution for that: constructor(props) {
super(props);
this.state = {
inputText: '',
currentUrl: ''
};
this.handleChange = this.handleChange.bind(this);
this.updateUrl = this.updateUrl.bind(this);
// Debounce
this.updateUrl = _.debounce(this.updateUrl, 500);
}
...
componentWillUnmount() {
this.updateUrl.cancel(); // or flush() in case we need to fire handler immediately.
} |
What about if the input is connected to a redux action and its value to the store. Is this a good solution: https://medium.com/@justintulk/debouncing-reacts-controlled-textareas-w-redux-lodash-4383084ca090 |
If anyone's looking for a decent solution to this then react-debounce-input works very nicely. It debounces the onChange event so your handler isn't triggered at all until data entry has stopped for the timeout period. I've only tried this out for text inputs so far. |
@gaearon this is very good solution, but in this case, we setState will render the component twice inside the same function? |
Is there a chance this will run into problems with the |
Debounce with Hooks import React, { useState, useCallback } from "react";
const debounce = (fn, delay) => {
let timeoutId;
return function(...args) {
clearInterval(timeoutId);
timeoutId = setTimeout(() => fn.apply(this, args), delay);
};
};
function SomeComponent() {
const [input, setInput] = useState("");
const [videoUrl, setVideoUrl] = useState("");
const debounceCallback = useCallback(
debounce(value => {
setVideoUrl(value);
}, 1000),
[]
);
const onInputChangeHandler = ({ target: { value } }) => {
setInput(value);
debounceCallback(value);
};
return (
<div>
<input value={input} onChange={onInputChangeHandler} />
<p>{videoUrl}</p>
</div>
);
}
export default SomeComponent; |
Extend useState hook import { useState } from "react";
import _ from "underscore"
export const useDebouncedState = (initialState, durationInMs = 500) => {
const [internalState, setInternalState] = useState(initialState);
const debouncedFunction = _.debounce(setInternalState, durationInMs);
return [internalState, debouncedFunction];
};
export default useDebouncedState; Use hook import useDebouncedState from "../hooks/useDebouncedState"
//...
const [usernameFilter, setUsernameFilter] = useDebouncedState("")
//...
<input id="username" type="text" onChange={e => setUsernameFilter(e.target.value)}></input> |
@shyam-arora thank you, your solution works like a charm !!! |
Hello. I have some problem about text input and onChange event. I need debouncing user input, but if I debounce onChange handle, there's no e.target and no opportunity for extract input value. How can I hack this situation?
Example: http://jsfiddle.net/e6JQn/
The text was updated successfully, but these errors were encountered: