Skip to content

Commit c2f4ef9

Browse files
committed
Do not accept incoming props for input value during debounce periods
1 parent 342126a commit c2f4ef9

File tree

4 files changed

+27
-31
lines changed

4 files changed

+27
-31
lines changed

components/dash-core-components/src/components/Input.react.js

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ export default class Input extends PureComponent {
3333

3434
UNSAFE_componentWillReceiveProps(nextProps) {
3535
const {value} = this.input.current;
36+
if (this.state?.pendingEvent && nextProps.value !== value) {
37+
// avoid updating the input while awaiting a debounced event
38+
return;
39+
}
3640
const valueAsNumber = convert(value);
3741
this.setInputValue(
3842
isNil(valueAsNumber) ? value : valueAsNumber,
@@ -124,18 +128,17 @@ export default class Input extends PureComponent {
124128
}
125129
}
126130

127-
debounceEvent() {
131+
debounceEvent(time = 0.5) {
128132
const {value} = this.input.current;
129-
let {debounce} = this.props;
130-
debounce = Number.isFinite(debounce) ? debounce * 1000 : 500;
133+
const MILLISECONDS = 1000;
134+
time = time * MILLISECONDS;
131135

132136
window.clearTimeout(this.state?.pendingEvent);
133137
const pendingEvent = window.setTimeout(() => {
134138
this.onEvent();
135-
}, debounce);
139+
}, time);
136140

137141
this.setState({
138-
...this.state,
139142
value,
140143
pendingEvent,
141144
});
@@ -158,29 +161,23 @@ export default class Input extends PureComponent {
158161
});
159162
this.input.current.checkValidity();
160163
}
161-
return this.props.debounce === true && e.key === 'Enter' && this.onEvent();
164+
return (
165+
this.props.debounce === true && e.key === 'Enter' && this.onEvent()
166+
);
162167
}
163168

164169
onChange() {
165-
if (this.props.debounce) {
166-
if (this.props.type !== 'number') {
167-
// this.setState({value: this.input.current.value});
170+
const {debounce} = this.props;
171+
if (debounce) {
172+
if (Number.isFinite(debounce)) {
173+
this.debounceEvent(debounce);
168174
}
169-
if (typeof this.props.debounce === 'number') {
170-
this.debounceEvent();
175+
if (this.props.type !== 'number') {
176+
this.setState({value: this.input.current.value});
171177
}
172178
} else {
173179
this.onEvent();
174180
}
175-
176-
177-
// if (!this.props.debounce) {
178-
// this.onEvent();
179-
// } else if (this.props.type !== 'number') {
180-
// this.setState({value: this.input.current.value});
181-
// } else if (typeof this.props.debounce === 'number') {
182-
// this.debounceEvent();
183-
// }
184181
}
185182
}
186183

@@ -221,8 +218,8 @@ Input.propTypes = {
221218
/**
222219
* If true, changes to input will be sent back to the Dash server only on enter or when losing focus.
223220
* If it's false, it will send the value back on every change.
224-
* If a number, it will wait for the user to stop changing the input for that number of seconds before
225-
* sending the value back
221+
* If a number, it will not send anything back to the Dash server until the user has stopped
222+
* typing for that number of seconds.
226223
*/
227224
debounce: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]),
228225

components/dash-core-components/tests/integration/input/conftest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ def range_out(val):
5858

5959
yield app
6060

61+
6162
@pytest.fixture(scope="module")
6263
def debounce_text_app():
6364
app = Dash(__name__)
@@ -69,7 +70,6 @@ def debounce_text_app():
6970
placeholder="long wait",
7071
),
7172
html.Div(id="div-slow"),
72-
7373
dcc.Input(
7474
id="input-fast",
7575
debounce=0.25,
@@ -88,6 +88,7 @@ def render(slow_val, fast_val):
8888

8989
yield app
9090

91+
9192
@pytest.fixture(scope="module")
9293
def debounce_number_app():
9394
app = Dash(__name__)
@@ -100,7 +101,6 @@ def debounce_number_app():
100101
placeholder="long wait",
101102
),
102103
html.Div(id="div-slow"),
103-
104104
dcc.Input(
105105
id="input-fast",
106106
debounce=0.25,

components/dash-core-components/tests/integration/input/test_debounce.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
from selenium.webdriver.common.by import By
44
import pytest
55

6+
67
def test_debounce_text_by_time(dash_dcc, debounce_text_app):
78
dash_dcc.start_server(debounce_text_app)
89

910
# expect that a long debounce does not call back in a short amount of time
1011
elem = dash_dcc.find_element("#input-slow")
11-
elem.send_keys('unit test slow')
12+
elem.send_keys("unit test slow")
1213
with pytest.raises(TimeoutException):
1314
WebDriverWait(dash_dcc.driver, timeout=1).until(
1415
lambda d: d.find_element(By.XPATH, "//*[text()='unit test slow']")
@@ -19,10 +20,9 @@ def test_debounce_text_by_time(dash_dcc, debounce_text_app):
1920
"#div-slow", "unit test slow"
2021
), "long debounce is eventually called back"
2122

22-
2323
# expect that a short debounce calls back within a short amount of time
2424
elem = dash_dcc.find_element("#input-fast")
25-
elem.send_keys('unit test fast')
25+
elem.send_keys("unit test fast")
2626
WebDriverWait(dash_dcc.driver, timeout=1).until(
2727
lambda d: d.find_element(By.XPATH, "//*[text()='unit test fast']")
2828
)
@@ -35,7 +35,7 @@ def test_debounce_number_by_time(dash_dcc, debounce_number_app):
3535

3636
# expect that a long debounce does not call back in a short amount of time
3737
elem = dash_dcc.find_element("#input-slow")
38-
elem.send_keys('12345')
38+
elem.send_keys("12345")
3939
with pytest.raises(TimeoutException):
4040
WebDriverWait(dash_dcc.driver, timeout=1).until(
4141
lambda d: d.find_element(By.XPATH, "//*[text()='12345']")
@@ -46,10 +46,9 @@ def test_debounce_number_by_time(dash_dcc, debounce_number_app):
4646
"#div-slow", "12345"
4747
), "long debounce is eventually called back"
4848

49-
5049
# expect that a short debounce calls back within a short amount of time
5150
elem = dash_dcc.find_element("#input-fast")
52-
elem.send_keys('67890')
51+
elem.send_keys("67890")
5352
WebDriverWait(dash_dcc.driver, timeout=1).until(
5453
lambda d: d.find_element(By.XPATH, "//*[text()='67890']")
5554
)

tests/integration/devtools/test_props_check.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"fail": True,
88
"name": 'simple "not a boolean" check',
99
"component": dcc.Input,
10-
"props": {"debounce": 0},
10+
"props": {"multiple": 0},
1111
},
1212
"missing-required-nested-prop": {
1313
"fail": True,

0 commit comments

Comments
 (0)