Skip to content

Commit 3085db7

Browse files
committed
add option to unmountBeforeUpdate
this is less than ideal - as such #461 has been created in order to try and track down what the problem is. in short victory charts do not update unless explicitely unmounted before updating. ReactDOM.render should work as expected, but for whatever reason it is not here.
1 parent ebf8640 commit 3085db7

File tree

8 files changed

+53
-12
lines changed

8 files changed

+53
-12
lines changed

docs/source/examples/simple_dashboard.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66
from idom.widgets import Input
77

88

9-
victory = idom.web.module_from_template("react", "victory", fallback="⌛")
9+
victory = idom.web.module_from_template(
10+
"react",
11+
"victory-line",
12+
fallback="loading...",
13+
unmount_before_update=True,
14+
)
1015
VictoryLine = idom.web.export(victory, "VictoryLine")
1116

1217

@@ -77,8 +82,8 @@ def update_value(value):
7782
set_value_callback(value)
7883

7984
return idom.html.fieldset(
80-
{"className": "number-input-container"},
81-
[idom.html.legend({"style": {"fontSize": "medium"}}, label)],
85+
{"class": "number-input-container"},
86+
[idom.html.legend({"style": {"font-size": "medium"}}, label)],
8287
Input(update_value, "number", value, attributes=attrs, cast=float),
8388
Input(update_value, "range", value, attributes=attrs, cast=float),
8489
)

docs/source/examples/victory_chart.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import idom
22

33

4-
victory = idom.web.module_from_template("react", "victory", fallback="⌛")
4+
victory = idom.web.module_from_template("react", "victory-line", fallback="⌛")
55
VictoryBar = idom.web.export(victory, "VictoryBar")
66

77
bar_style = {"parent": {"width": "500px"}, "data": {"fill": "royalblue"}}

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,20 +72,25 @@ function ImportedElement({ model }) {
7272
}
7373
}
7474

75-
function RenderImportedElement() {
75+
function RenderImportedElement({ model, importSource }) {
7676
const config = React.useContext(LayoutConfigContext);
7777
const mountPoint = React.useRef(null);
7878
const sourceBinding = React.useRef(null);
7979

8080
React.useEffect(() => {
8181
sourceBinding.current = importSource.bind(mountPoint.current);
82-
return () => {
83-
sourceBinding.current.unmount();
84-
};
82+
if (!importSource.data.unmountBeforeUpdate) {
83+
return sourceBinding.current.unmount;
84+
}
8585
}, []);
8686

8787
// this effect must run every time in case the model has changed
88-
React.useEffect(() => sourceBinding.current.render(model));
88+
React.useEffect(() => {
89+
sourceBinding.current.render(model);
90+
if (importSource.data.unmountBeforeUpdate) {
91+
return sourceBinding.current.unmount;
92+
}
93+
});
8994

9095
return html`<div ref=${mountPoint} />`;
9196
}
@@ -145,6 +150,7 @@ function loadImportSource(config, importSource) {
145150
.then((module) => {
146151
if (typeof module.bind == "function") {
147152
return {
153+
data: importSource,
148154
bind: (node) => {
149155
const binding = module.bind(node, config);
150156
if (

src/idom/core/vdom.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
"if": {"not": {"type": "null"}},
7878
"then": {"$ref": "#/definitions/elementOrString"},
7979
},
80+
"unmountBeforeUpdate": {"type": "boolean"},
8081
},
8182
"required": ["source"],
8283
},
@@ -289,6 +290,7 @@ class ImportSourceDict(TypedDict):
289290
source: str
290291
fallback: Any
291292
sourceType: str # noqa
293+
unmountBeforeUpdate: bool # noqa
292294

293295

294296
class _OptionalVdomJson(TypedDict, total=False):

src/idom/web/module.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def module_from_url(
3636
fallback: Optional[Any] = None,
3737
resolve_exports: bool = IDOM_DEBUG_MODE.current,
3838
resolve_exports_depth: int = 5,
39+
unmount_before_update: bool = False,
3940
) -> WebModule:
4041
"""Load a :class:`WebModule` from a :data:`URL_SOURCE`
4142
@@ -49,6 +50,11 @@ def module_from_url(
4950
Whether to try and find all the named exports of this module.
5051
resolve_exports_depth:
5152
How deeply to search for those exports.
53+
unmount_before_update:
54+
Cause the component to be unmounted before each update. This option should
55+
only be used if the imported package failes to re-render when props change.
56+
Using this option has negative performance consequences since all DOM
57+
elements must be changed on each render. See :issue:`461` for more info.
5258
"""
5359
return WebModule(
5460
source=url,
@@ -60,6 +66,7 @@ def module_from_url(
6066
if resolve_exports
6167
else None
6268
),
69+
unmount_before_update=unmount_before_update,
6370
)
6471

6572

@@ -70,6 +77,7 @@ def module_from_template(
7077
fallback: Optional[Any] = None,
7178
resolve_exports: bool = IDOM_DEBUG_MODE.current,
7279
resolve_exports_depth: int = 5,
80+
unmount_before_update: bool = False,
7381
) -> WebModule:
7482
"""Create a :class:`WebModule` from a framework template
7583
@@ -98,6 +106,11 @@ def module_from_template(
98106
Whether to try and find all the named exports of this module.
99107
resolve_exports_depth:
100108
How deeply to search for those exports.
109+
unmount_before_update:
110+
Cause the component to be unmounted before each update. This option should
111+
only be used if the imported package failes to re-render when props change.
112+
Using this option has negative performance consequences since all DOM
113+
elements must be changed on each render. See :issue:`461` for more info.
101114
"""
102115
# We do this since the package may be any valid URL path. Thus we may need to strip
103116
# object parameters or query information so we save the resulting template under the
@@ -129,6 +142,7 @@ def module_from_template(
129142
if resolve_exports
130143
else None
131144
),
145+
unmount_before_update=unmount_before_update,
132146
)
133147

134148

@@ -139,6 +153,7 @@ def module_from_file(
139153
resolve_exports: bool = IDOM_DEBUG_MODE.current,
140154
resolve_exports_depth: int = 5,
141155
symlink: bool = False,
156+
unmount_before_update: bool = False,
142157
) -> WebModule:
143158
"""Load a :class:`WebModule` from a :data:`URL_SOURCE` using a known framework
144159
@@ -156,6 +171,11 @@ def module_from_file(
156171
Whether to try and find all the named exports of this module.
157172
resolve_exports_depth:
158173
How deeply to search for those exports.
174+
unmount_before_update:
175+
Cause the component to be unmounted before each update. This option should
176+
only be used if the imported package failes to re-render when props change.
177+
Using this option has negative performance consequences since all DOM
178+
elements must be changed on each render. See :issue:`461` for more info.
159179
"""
160180
source_file = Path(file)
161181
target_file = _web_module_path(name)
@@ -179,6 +199,7 @@ def module_from_file(
179199
if resolve_exports
180200
else None
181201
),
202+
unmount_before_update=unmount_before_update,
182203
)
183204

184205

@@ -189,6 +210,7 @@ class WebModule:
189210
default_fallback: Optional[Any]
190211
export_names: Optional[Set[str]]
191212
file: Optional[Path]
213+
unmount_before_update: bool
192214

193215

194216
@overload
@@ -250,7 +272,10 @@ def export(
250272

251273

252274
def _make_export(
253-
web_module: WebModule, name: str, fallback: Optional[Any], allow_children: bool
275+
web_module: WebModule,
276+
name: str,
277+
fallback: Optional[Any],
278+
allow_children: bool,
254279
) -> VdomDictConstructor:
255280
return partial(
256281
make_vdom_constructor(
@@ -261,6 +286,7 @@ def _make_export(
261286
source=web_module.source,
262287
sourceType=web_module.source_type,
263288
fallback=(fallback or web_module.default_fallback),
289+
unmountBeforeUpdate=web_module.unmount_before_update,
264290
),
265291
)
266292

src/idom/web/utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212

1313
def module_name_suffix(name: str) -> str:
14+
if name.startswith("@"):
15+
name = name[1:]
1416
head, _, tail = name.partition("@") # handle version identifier
1517
version, _, tail = tail.partition("/") # get section after version
1618
return PurePosixPath(tail or head).suffix or ".js"

tests/test_server/test_common/test_per_client_state.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def Counter():
6666

6767

6868
def test_module_from_template(driver, display):
69-
victory = idom.web.module_from_template("react", "[email protected]")
69+
victory = idom.web.module_from_template("react", "victory-bar@35.4.0")
7070
VictoryBar = idom.web.export(victory, "VictoryBar")
7171
display(VictoryBar)
7272
driver.find_element_by_class_name("VictoryContainer")

tests/test_web/test_module.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def test_module_from_template_where_template_does_not_exist():
7979

8080

8181
def test_module_from_template(driver, display):
82-
victory = idom.web.module_from_template("react", "[email protected]")
82+
victory = idom.web.module_from_template("react", "victory-bar@35.4.0")
8383
VictoryBar = idom.web.export(victory, "VictoryBar")
8484
display(VictoryBar)
8585
wait = WebDriverWait(driver, 10)

0 commit comments

Comments
 (0)