Skip to content

Commit 5648245

Browse files
committed
fix flicker with observedValues buffer
1 parent a029189 commit 5648245

File tree

2 files changed

+56
-4
lines changed

2 files changed

+56
-4
lines changed

src/client/packages/idom-client-react/src/components.js

+20-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ function UserInputElement({ model }) {
8181
// order to allow all changes committed by the user to be recorded in the order they
8282
// occur. If we don't the user may commit multiple changes before we render next
8383
// causing the content of prior changes to be overwritten by subsequent changes.
84-
const value = props.value;
84+
let value = props.value;
8585
delete props.value;
8686

8787
// Instead of controlling the value, we set it in an effect.
@@ -91,6 +91,25 @@ function UserInputElement({ model }) {
9191
}
9292
}, [ref.current, value]);
9393

94+
// Track a buffer of observed values in order to avoid flicker
95+
const observedValues = React.useState([])[0];
96+
if (observedValues) {
97+
if (value === observedValues[0]) {
98+
observedValues.shift();
99+
value = observedValues[observedValues.length - 1];
100+
} else {
101+
observedValues.length = 0;
102+
}
103+
}
104+
105+
const givenOnChange = props.onChange;
106+
if (typeof givenOnChange === "function") {
107+
props.onChange = (event) => {
108+
observedValues.push(event.target.value);
109+
givenOnChange(event);
110+
};
111+
}
112+
94113
// Use createElement here to avoid warning about variable numbers of children not
95114
// having keys. Warning about this must now be the responsibility of the server
96115
// providing the models instead of the client rendering them.

tests/test_client.py

+36-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
import asyncio
2+
import time
13
from pathlib import Path
24

35
import idom
46
from idom.testing import ServerMountPoint
7+
from tests.driver_utils import send_keys
58

69

710
JS_DIR = Path(__file__).parent / "js"
@@ -71,14 +74,44 @@ def ButtonWithChangingColor():
7174

7275
button = driver.find_element("id", "my-button")
7376

74-
assert get_style(button)["background-color"] == "red"
77+
assert _get_style(button)["background-color"] == "red"
7578

7679
for color in ["blue", "red"] * 2:
7780
button.click()
78-
driver_wait.until(lambda _: get_style(button)["background-color"] == color)
81+
driver_wait.until(lambda _: _get_style(button)["background-color"] == color)
7982

8083

81-
def get_style(element):
84+
def _get_style(element):
8285
items = element.get_attribute("style").split(";")
8386
pairs = [item.split(":", 1) for item in map(str.strip, items) if item]
8487
return {key.strip(): value.strip() for key, value in pairs}
88+
89+
90+
def test_slow_server_response_on_input_change(display, driver, driver_wait):
91+
"""A delay server-side could cause input values to be overwritten.
92+
93+
For more info see: https://github.com/idom-team/idom/issues/684
94+
"""
95+
96+
delay = 0.2
97+
98+
@idom.component
99+
def SomeComponent():
100+
value, set_value = idom.hooks.use_state("")
101+
102+
async def handle_change(event):
103+
await asyncio.sleep(delay)
104+
set_value(event["target"]["value"])
105+
106+
return idom.html.input({"onChange": handle_change, "id": "test-input"})
107+
108+
display(SomeComponent)
109+
110+
inp = driver.find_element("id", "test-input")
111+
112+
text = "hello"
113+
send_keys(inp, text)
114+
115+
time.sleep(delay * len(text) * 1.1)
116+
117+
driver_wait.until(lambda _: inp.get_attribute("value") == "hello")

0 commit comments

Comments
 (0)